Skip to content

Commit

Permalink
Merge branch 'nfc-s3fwrn5-support-a-uart-interface'
Browse files Browse the repository at this point in the history
Bongsu Jeon says:

====================
nfc: s3fwrn5: Support a UART interface

S3FWRN82 is the Samsung's NFC chip that supports the UART communication.
Before adding the UART driver module, I did refactoring the s3fwrn5_i2c
module to reuse the common blocks.
====================

Link: https://lore.kernel.org/r/1606909661-3814-1-git-send-email-bongsu.jeon@samsung.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Dec 3, 2020
2 parents 32e4170 + 3f52c2c commit db77471
Show file tree
Hide file tree
Showing 7 changed files with 390 additions and 82 deletions.
31 changes: 28 additions & 3 deletions Documentation/devicetree/bindings/net/nfc/samsung,s3fwrn5.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ maintainers:

properties:
compatible:
const: samsung,s3fwrn5-i2c
enum:
- samsung,s3fwrn5-i2c
- samsung,s3fwrn82

en-gpios:
maxItems: 1
Expand Down Expand Up @@ -47,10 +49,19 @@ additionalProperties: false
required:
- compatible
- en-gpios
- interrupts
- reg
- wake-gpios

allOf:
- if:
properties:
compatible:
contains:
const: samsung,s3fwrn5-i2c
then:
required:
- interrupts
- reg

examples:
- |
#include <dt-bindings/gpio/gpio.h>
Expand All @@ -71,3 +82,17 @@ examples:
wake-gpios = <&gpj0 2 GPIO_ACTIVE_HIGH>;
};
};
# UART example on Raspberry Pi
- |
uart0 {
status = "okay";
nfc {
compatible = "samsung,s3fwrn82";
en-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
wake-gpios = <&gpio 16 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
12 changes: 12 additions & 0 deletions drivers/nfc/s3fwrn5/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ config NFC_S3FWRN5_I2C
To compile this driver as a module, choose m here. The module will
be called s3fwrn5_i2c.ko.
Say N if unsure.

config NFC_S3FWRN82_UART
tristate "Samsung S3FWRN82 UART support"
depends on NFC_NCI && SERIAL_DEV_BUS
select NFC_S3FWRN5
help
This module adds support for a UART interface to the S3FWRN82 chip.
Select this if your platform is using the UART bus.

To compile this driver as a module, choose m here. The module will
be called s3fwrn82_uart.ko.
Say N if unsure.
4 changes: 3 additions & 1 deletion drivers/nfc/s3fwrn5/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
# Makefile for Samsung S3FWRN5 NFC driver
#

s3fwrn5-objs = core.o firmware.o nci.o
s3fwrn5-objs = core.o firmware.o nci.o phy_common.o
s3fwrn5_i2c-objs = i2c.o
s3fwrn82_uart-objs = uart.o

obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5.o
obj-$(CONFIG_NFC_S3FWRN5_I2C) += s3fwrn5_i2c.o
obj-$(CONFIG_NFC_S3FWRN82_UART) += s3fwrn82_uart.o
117 changes: 39 additions & 78 deletions drivers/nfc/s3fwrn5/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,83 +15,38 @@

#include <net/nfc/nfc.h>

#include "s3fwrn5.h"
#include "phy_common.h"

#define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c"

#define S3FWRN5_EN_WAIT_TIME 150

struct s3fwrn5_i2c_phy {
struct phy_common common;
struct i2c_client *i2c_dev;
struct nci_dev *ndev;

int gpio_en;
int gpio_fw_wake;

struct mutex mutex;

enum s3fwrn5_mode mode;
unsigned int irq_skip:1;
};

static void s3fwrn5_i2c_set_wake(void *phy_id, bool wake)
{
struct s3fwrn5_i2c_phy *phy = phy_id;

mutex_lock(&phy->mutex);
gpio_set_value(phy->gpio_fw_wake, wake);
msleep(S3FWRN5_EN_WAIT_TIME/2);
mutex_unlock(&phy->mutex);
}

static void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode)
{
struct s3fwrn5_i2c_phy *phy = phy_id;

mutex_lock(&phy->mutex);
mutex_lock(&phy->common.mutex);

if (phy->mode == mode)
if (s3fwrn5_phy_power_ctrl(&phy->common, mode) == false)
goto out;

phy->mode = mode;

gpio_set_value(phy->gpio_en, 1);
gpio_set_value(phy->gpio_fw_wake, 0);
if (mode == S3FWRN5_MODE_FW)
gpio_set_value(phy->gpio_fw_wake, 1);

if (mode != S3FWRN5_MODE_COLD) {
msleep(S3FWRN5_EN_WAIT_TIME);
gpio_set_value(phy->gpio_en, 0);
msleep(S3FWRN5_EN_WAIT_TIME/2);
}

phy->irq_skip = true;

out:
mutex_unlock(&phy->mutex);
}

static enum s3fwrn5_mode s3fwrn5_i2c_get_mode(void *phy_id)
{
struct s3fwrn5_i2c_phy *phy = phy_id;
enum s3fwrn5_mode mode;

mutex_lock(&phy->mutex);

mode = phy->mode;

mutex_unlock(&phy->mutex);

return mode;
mutex_unlock(&phy->common.mutex);
}

static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
{
struct s3fwrn5_i2c_phy *phy = phy_id;
int ret;

mutex_lock(&phy->mutex);
mutex_lock(&phy->common.mutex);

phy->irq_skip = false;

Expand All @@ -102,7 +57,7 @@ static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len);
}

mutex_unlock(&phy->mutex);
mutex_unlock(&phy->common.mutex);

if (ret < 0)
return ret;
Expand All @@ -114,9 +69,9 @@ static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
}

static const struct s3fwrn5_phy_ops i2c_phy_ops = {
.set_wake = s3fwrn5_i2c_set_wake,
.set_wake = s3fwrn5_phy_set_wake,
.set_mode = s3fwrn5_i2c_set_mode,
.get_mode = s3fwrn5_i2c_get_mode,
.get_mode = s3fwrn5_phy_get_mode,
.write = s3fwrn5_i2c_write,
};

Expand All @@ -128,7 +83,7 @@ static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy)
char hdr[4];
int ret;

hdr_size = (phy->mode == S3FWRN5_MODE_NCI) ?
hdr_size = (phy->common.mode == S3FWRN5_MODE_NCI) ?
NCI_CTRL_HDR_SIZE : S3FWRN5_FW_HDR_SIZE;
ret = i2c_master_recv(phy->i2c_dev, hdr, hdr_size);
if (ret < 0)
Expand All @@ -137,7 +92,7 @@ static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy)
if (ret < hdr_size)
return -EBADMSG;

data_len = (phy->mode == S3FWRN5_MODE_NCI) ?
data_len = (phy->common.mode == S3FWRN5_MODE_NCI) ?
((struct nci_ctrl_hdr *)hdr)->plen :
((struct s3fwrn5_fw_header *)hdr)->len;

Expand All @@ -157,24 +112,24 @@ static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy)
}

out:
return s3fwrn5_recv_frame(phy->ndev, skb, phy->mode);
return s3fwrn5_recv_frame(phy->common.ndev, skb, phy->common.mode);
}

static irqreturn_t s3fwrn5_i2c_irq_thread_fn(int irq, void *phy_id)
{
struct s3fwrn5_i2c_phy *phy = phy_id;

if (!phy || !phy->ndev) {
if (!phy || !phy->common.ndev) {
WARN_ON_ONCE(1);
return IRQ_NONE;
}

mutex_lock(&phy->mutex);
mutex_lock(&phy->common.mutex);

if (phy->irq_skip)
goto out;

switch (phy->mode) {
switch (phy->common.mode) {
case S3FWRN5_MODE_NCI:
case S3FWRN5_MODE_FW:
s3fwrn5_i2c_read(phy);
Expand All @@ -184,7 +139,7 @@ static irqreturn_t s3fwrn5_i2c_irq_thread_fn(int irq, void *phy_id)
}

out:
mutex_unlock(&phy->mutex);
mutex_unlock(&phy->common.mutex);

return IRQ_HANDLED;
}
Expand All @@ -197,19 +152,23 @@ static int s3fwrn5_i2c_parse_dt(struct i2c_client *client)
if (!np)
return -ENODEV;

phy->gpio_en = of_get_named_gpio(np, "en-gpios", 0);
if (!gpio_is_valid(phy->gpio_en)) {
phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
if (!gpio_is_valid(phy->common.gpio_en)) {
/* Support also deprecated property */
phy->gpio_en = of_get_named_gpio(np, "s3fwrn5,en-gpios", 0);
if (!gpio_is_valid(phy->gpio_en))
phy->common.gpio_en = of_get_named_gpio(np,
"s3fwrn5,en-gpios",
0);
if (!gpio_is_valid(phy->common.gpio_en))
return -ENODEV;
}

phy->gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
if (!gpio_is_valid(phy->gpio_fw_wake)) {
phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
if (!gpio_is_valid(phy->common.gpio_fw_wake)) {
/* Support also deprecated property */
phy->gpio_fw_wake = of_get_named_gpio(np, "s3fwrn5,fw-gpios", 0);
if (!gpio_is_valid(phy->gpio_fw_wake))
phy->common.gpio_fw_wake = of_get_named_gpio(np,
"s3fwrn5,fw-gpios",
0);
if (!gpio_is_valid(phy->common.gpio_fw_wake))
return -ENODEV;
}

Expand All @@ -226,8 +185,8 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client,
if (!phy)
return -ENOMEM;

mutex_init(&phy->mutex);
phy->mode = S3FWRN5_MODE_COLD;
mutex_init(&phy->common.mutex);
phy->common.mode = S3FWRN5_MODE_COLD;
phy->irq_skip = true;

phy->i2c_dev = client;
Expand All @@ -237,25 +196,27 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client,
if (ret < 0)
return ret;

ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
GPIOF_OUT_INIT_HIGH, "s3fwrn5_en");
ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->common.gpio_en,
GPIOF_OUT_INIT_HIGH, "s3fwrn5_en");
if (ret < 0)
return ret;

ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw_wake,
GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake");
ret = devm_gpio_request_one(&phy->i2c_dev->dev,
phy->common.gpio_fw_wake,
GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake");
if (ret < 0)
return ret;

ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops);
ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->i2c_dev->dev,
&i2c_phy_ops);
if (ret < 0)
return ret;

ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL,
s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
S3FWRN5_I2C_DRIVER_NAME, phy);
if (ret)
s3fwrn5_remove(phy->ndev);
s3fwrn5_remove(phy->common.ndev);

return ret;
}
Expand All @@ -264,7 +225,7 @@ static int s3fwrn5_i2c_remove(struct i2c_client *client)
{
struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);

s3fwrn5_remove(phy->ndev);
s3fwrn5_remove(phy->common.ndev);

return 0;
}
Expand Down
Loading

0 comments on commit db77471

Please sign in to comment.