Commit db774712 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'nfc-s3fwrn5-support-a-uart-interface'

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: default avatarJakub Kicinski <kuba@kernel.org>
parents 32e41702 3f52c2cb
Loading
Loading
Loading
Loading
+28 −3
Original line number Diff line number Diff line
@@ -12,7 +12,9 @@ maintainers:

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

  en-gpios:
    maxItems: 1
@@ -47,9 +49,18 @@ additionalProperties: false
required:
  - compatible
  - en-gpios
  - wake-gpios

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

examples:
  - |
@@ -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 −0
Original line number Diff line number Diff line
@@ -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.
+3 −1
Original line number Diff line number Diff line
@@ -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
+39 −78
Original line number Diff line number Diff line
@@ -15,75 +15,30 @@

#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)
@@ -91,7 +46,7 @@ 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;

@@ -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;
@@ -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,
};

@@ -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)
@@ -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;

@@ -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);
@@ -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;
}
@@ -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;
	}

@@ -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;
@@ -237,17 +196,19 @@ 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,
	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,
	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;

@@ -255,7 +216,7 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client,
		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;
}
@@ -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;
}
+75 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Link Layer for Samsung S3FWRN5 NCI based Driver
 *
 * Copyright (C) 2015 Samsung Electrnoics
 * Robert Baldyga <r.baldyga@samsung.com>
 * Copyright (C) 2020 Samsung Electrnoics
 * Bongsu Jeon <bongsu.jeon@samsung.com>
 */

#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/module.h>

#include "phy_common.h"

void s3fwrn5_phy_set_wake(void *phy_id, bool wake)
{
	struct phy_common *phy = phy_id;

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

bool s3fwrn5_phy_power_ctrl(struct phy_common *phy, enum s3fwrn5_mode mode)
{
	if (phy->mode == mode)
		return false;

	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);
	}

	return true;
}
EXPORT_SYMBOL(s3fwrn5_phy_power_ctrl);

void s3fwrn5_phy_set_mode(void *phy_id, enum s3fwrn5_mode mode)
{
	struct phy_common *phy = phy_id;

	mutex_lock(&phy->mutex);

	s3fwrn5_phy_power_ctrl(phy, mode);

	mutex_unlock(&phy->mutex);
}
EXPORT_SYMBOL(s3fwrn5_phy_set_mode);

enum s3fwrn5_mode s3fwrn5_phy_get_mode(void *phy_id)
{
	struct phy_common *phy = phy_id;
	enum s3fwrn5_mode mode;

	mutex_lock(&phy->mutex);

	mode = phy->mode;

	mutex_unlock(&phy->mutex);

	return mode;
}
EXPORT_SYMBOL(s3fwrn5_phy_get_mode);
Loading