Skip to content

Commit

Permalink
nfc: s3fwrn5: Add driver for Samsung S3FWRN5 NFC Chip
Browse files Browse the repository at this point in the history
Add driver for Samsung S3FWRN5 NFC controller.
S3FWRN5 is using NCI protocol and I2C communication interface.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Robert Baldyga authored and Samuel Ortiz committed Aug 20, 2015
1 parent 025a0cb commit c04c674
Show file tree
Hide file tree
Showing 13 changed files with 1,565 additions and 0 deletions.
27 changes: 27 additions & 0 deletions Documentation/devicetree/bindings/net/nfc/s3fwrn5.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
* Samsung S3FWRN5 NCI NFC Controller

Required properties:
- compatible: Should be "samsung,s3fwrn5-i2c".
- reg: address on the bus
- interrupt-parent: phandle for the interrupt gpio controller
- interrupts: GPIO interrupt to which the chip is connected
- s3fwrn5,en-gpios: Output GPIO pin used for enabling/disabling the chip
- s3fwrn5,fw-gpios: Output GPIO pin used to enter firmware mode and
sleep/wakeup control

Example:

&hsi2c_4 {
status = "okay";
s3fwrn5@27 {
compatible = "samsung,s3fwrn5-i2c";

reg = <0x27>;

interrupt-parent = <&gpa1>;
interrupts = <3 0 0>;

s3fwrn5,en-gpios = <&gpf1 4 0>;
s3fwrn5,fw-gpios = <&gpj0 2 0>;
};
};
6 changes: 6 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -8871,6 +8871,12 @@ L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/i2c/s5k5baf.c

SAMSUNG S3FWRN5 NFC DRIVER
M: Robert Baldyga <r.baldyga@samsung.com>
L: linux-nfc@lists.01.org (moderated for non-subscribers)
S: Supported
F: drivers/nfc/s3fwrn5

SAMSUNG SOC CLOCK DRIVERS
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
M: Tomasz Figa <tomasz.figa@gmail.com>
Expand Down
1 change: 1 addition & 0 deletions drivers/nfc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ source "drivers/nfc/nfcmrvl/Kconfig"
source "drivers/nfc/st21nfca/Kconfig"
source "drivers/nfc/st-nci/Kconfig"
source "drivers/nfc/nxp-nci/Kconfig"
source "drivers/nfc/s3fwrn5/Kconfig"
endmenu
1 change: 1 addition & 0 deletions drivers/nfc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/
obj-$(CONFIG_NFC_ST_NCI) += st-nci/
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/
19 changes: 19 additions & 0 deletions drivers/nfc/s3fwrn5/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
config NFC_S3FWRN5
tristate
---help---
Core driver for Samsung S3FWRN5 NFC chip. Contains core utilities
of chip. It's intended to be used by PHYs to avoid duplicating lots
of common code.

config NFC_S3FWRN5_I2C
tristate "Samsung S3FWRN5 I2C support"
depends on NFC_NCI && I2C
select NFC_S3FWRN5
default n
---help---
This module adds support for an I2C interface to the S3FWRN5 chip.
Select this if your platform is using the I2C bus.

To compile this driver as a module, choose m here. The module will
be called s3fwrn5_i2c.ko.
Say N if unsure.
11 changes: 11 additions & 0 deletions drivers/nfc/s3fwrn5/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# Makefile for Samsung S3FWRN5 NFC driver
#

s3fwrn5-objs = core.o firmware.o nci.o
s3fwrn5_i2c-objs = i2c.o

obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5.o
obj-$(CONFIG_NFC_S3FWRN5_I2C) += s3fwrn5_i2c.o

ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
219 changes: 219 additions & 0 deletions drivers/nfc/s3fwrn5/core.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
* NCI based driver for Samsung S3FWRN5 NFC chip
*
* Copyright (C) 2015 Samsung Electrnoics
* Robert Baldyga <r.baldyga@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/

#include <linux/module.h>
#include <net/nfc/nci_core.h>

#include "s3fwrn5.h"
#include "firmware.h"
#include "nci.h"

#define S3FWRN5_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
NFC_PROTO_MIFARE_MASK | \
NFC_PROTO_FELICA_MASK | \
NFC_PROTO_ISO14443_MASK | \
NFC_PROTO_ISO14443_B_MASK | \
NFC_PROTO_ISO15693_MASK)

static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
{
bool need_update;
int ret;

s3fwrn5_fw_init(&info->fw_info, "sec_s3fwrn5_firmware.bin");

/* Update firmware */

s3fwrn5_set_wake(info, false);
s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);

ret = s3fwrn5_fw_setup(&info->fw_info);
if (ret < 0)
return ret;

need_update = s3fwrn5_fw_check_version(&info->fw_info,
info->ndev->manufact_specific_info);
if (!need_update)
goto out;

dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");

ret = s3fwrn5_fw_download(&info->fw_info);
if (ret < 0)
goto out;

/* Update RF configuration */

s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);

s3fwrn5_set_wake(info, true);
ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
s3fwrn5_set_wake(info, false);

out:
s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
s3fwrn5_fw_cleanup(&info->fw_info);
return ret;
}

static int s3fwrn5_nci_open(struct nci_dev *ndev)
{
struct s3fwrn5_info *info = nci_get_drvdata(ndev);

if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
return -EBUSY;

s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
s3fwrn5_set_wake(info, true);

return 0;
}

static int s3fwrn5_nci_close(struct nci_dev *ndev)
{
struct s3fwrn5_info *info = nci_get_drvdata(ndev);

s3fwrn5_set_wake(info, false);
s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);

return 0;
}

static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
{
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
int ret;

mutex_lock(&info->mutex);

if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
mutex_unlock(&info->mutex);
return -EINVAL;
}

ret = s3fwrn5_write(info, skb);
if (ret < 0)
kfree_skb(skb);

mutex_unlock(&info->mutex);
return ret;
}

static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
{
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
int ret;

ret = s3fwrn5_firmware_update(info);
if (ret < 0)
goto out;

/* NCI core reset */

s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
s3fwrn5_set_wake(info, true);

ret = nci_core_reset(info->ndev);
if (ret < 0)
goto out;

ret = nci_core_init(info->ndev);

out:
return ret;
}

static struct nci_ops s3fwrn5_nci_ops = {
.open = s3fwrn5_nci_open,
.close = s3fwrn5_nci_close,
.send = s3fwrn5_nci_send,
.post_setup = s3fwrn5_nci_post_setup,
};

int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
{
struct s3fwrn5_info *info;
int ret;

info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;

info->phy_id = phy_id;
info->pdev = pdev;
info->phy_ops = phy_ops;
info->max_payload = max_payload;
mutex_init(&info->mutex);

s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);

s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
&s3fwrn5_nci_ops.n_prop_ops);

info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
S3FWRN5_NFC_PROTOCOLS, 0, 0);
if (!info->ndev)
return -ENOMEM;

nci_set_parent_dev(info->ndev, pdev);
nci_set_drvdata(info->ndev, info);

ret = nci_register_device(info->ndev);
if (ret < 0) {
nci_free_device(info->ndev);
return ret;
}

info->fw_info.ndev = info->ndev;

*ndev = info->ndev;

return ret;
}
EXPORT_SYMBOL(s3fwrn5_probe);

void s3fwrn5_remove(struct nci_dev *ndev)
{
struct s3fwrn5_info *info = nci_get_drvdata(ndev);

s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);

nci_unregister_device(ndev);
nci_free_device(ndev);
}
EXPORT_SYMBOL(s3fwrn5_remove);

int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
enum s3fwrn5_mode mode)
{
switch (mode) {
case S3FWRN5_MODE_NCI:
return nci_recv_frame(ndev, skb);
case S3FWRN5_MODE_FW:
return s3fwrn5_fw_recv_frame(ndev, skb);
default:
return -ENODEV;
}
}
EXPORT_SYMBOL(s3fwrn5_recv_frame);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
Loading

0 comments on commit c04c674

Please sign in to comment.