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: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
db77471259
@ -12,7 +12,9 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: samsung,s3fwrn5-i2c
|
||||
enum:
|
||||
- samsung,s3fwrn5-i2c
|
||||
- samsung,s3fwrn82
|
||||
|
||||
en-gpios:
|
||||
maxItems: 1
|
||||
@ -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>
|
||||
@ -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";
|
||||
};
|
||||
};
|
||||
|
@ -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,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
|
||||
|
@ -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,
|
||||
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;
|
||||
|
||||
@ -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
drivers/nfc/s3fwrn5/phy_common.c
Normal file
75
drivers/nfc/s3fwrn5/phy_common.c
Normal file
@ -0,0 +1,75 @@
|
||||
// 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);
|
37
drivers/nfc/s3fwrn5/phy_common.h
Normal file
37
drivers/nfc/s3fwrn5/phy_common.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* 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>
|
||||
*/
|
||||
|
||||
#ifndef __NFC_S3FWRN5_PHY_COMMON_H
|
||||
#define __NFC_S3FWRN5_PHY_COMMON_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <net/nfc/nci_core.h>
|
||||
|
||||
#include "s3fwrn5.h"
|
||||
|
||||
#define S3FWRN5_EN_WAIT_TIME 20
|
||||
|
||||
struct phy_common {
|
||||
struct nci_dev *ndev;
|
||||
|
||||
int gpio_en;
|
||||
int gpio_fw_wake;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
enum s3fwrn5_mode mode;
|
||||
};
|
||||
|
||||
void s3fwrn5_phy_set_wake(void *phy_id, bool wake);
|
||||
bool s3fwrn5_phy_power_ctrl(struct phy_common *phy, enum s3fwrn5_mode mode);
|
||||
void s3fwrn5_phy_set_mode(void *phy_id, enum s3fwrn5_mode mode);
|
||||
enum s3fwrn5_mode s3fwrn5_phy_get_mode(void *phy_id);
|
||||
|
||||
#endif /* __NFC_S3FWRN5_PHY_COMMON_H */
|
196
drivers/nfc/s3fwrn5/uart.c
Normal file
196
drivers/nfc/s3fwrn5/uart.c
Normal file
@ -0,0 +1,196 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* UART Link Layer for S3FWRN82 NCI based Driver
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Robert Baldyga <r.baldyga@samsung.com>
|
||||
* Copyright (C) 2020 Samsung Electronics
|
||||
* Bongsu Jeon <bongsu.jeon@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nfc.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "phy_common.h"
|
||||
|
||||
#define S3FWRN82_NCI_HEADER 3
|
||||
#define S3FWRN82_NCI_IDX 2
|
||||
#define NCI_SKB_BUFF_LEN 258
|
||||
|
||||
struct s3fwrn82_uart_phy {
|
||||
struct phy_common common;
|
||||
struct serdev_device *ser_dev;
|
||||
struct sk_buff *recv_skb;
|
||||
};
|
||||
|
||||
static int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy = phy_id;
|
||||
int err;
|
||||
|
||||
err = serdev_device_write(phy->ser_dev,
|
||||
out->data, out->len,
|
||||
MAX_SCHEDULE_TIMEOUT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct s3fwrn5_phy_ops uart_phy_ops = {
|
||||
.set_wake = s3fwrn5_phy_set_wake,
|
||||
.set_mode = s3fwrn5_phy_set_mode,
|
||||
.get_mode = s3fwrn5_phy_get_mode,
|
||||
.write = s3fwrn82_uart_write,
|
||||
};
|
||||
|
||||
static int s3fwrn82_uart_read(struct serdev_device *serdev,
|
||||
const unsigned char *data,
|
||||
size_t count)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
skb_put_u8(phy->recv_skb, *data++);
|
||||
|
||||
if (phy->recv_skb->len < S3FWRN82_NCI_HEADER)
|
||||
continue;
|
||||
|
||||
if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER)
|
||||
< phy->recv_skb->data[S3FWRN82_NCI_IDX])
|
||||
continue;
|
||||
|
||||
s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb,
|
||||
phy->common.mode);
|
||||
phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
|
||||
if (!phy->recv_skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static const struct serdev_device_ops s3fwrn82_serdev_ops = {
|
||||
.receive_buf = s3fwrn82_uart_read,
|
||||
.write_wakeup = serdev_device_write_wakeup,
|
||||
};
|
||||
|
||||
static const struct of_device_id s3fwrn82_uart_of_match[] = {
|
||||
{ .compatible = "samsung,s3fwrn82", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
|
||||
|
||||
static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
|
||||
struct device_node *np = serdev->dev.of_node;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
|
||||
if (!gpio_is_valid(phy->common.gpio_en))
|
||||
return -ENODEV;
|
||||
|
||||
phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
|
||||
if (!gpio_is_valid(phy->common.gpio_fw_wake))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3fwrn82_uart_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
goto err_exit;
|
||||
|
||||
phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
|
||||
if (!phy->recv_skb)
|
||||
goto err_exit;
|
||||
|
||||
mutex_init(&phy->common.mutex);
|
||||
phy->common.mode = S3FWRN5_MODE_COLD;
|
||||
|
||||
phy->ser_dev = serdev;
|
||||
serdev_device_set_drvdata(serdev, phy);
|
||||
serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops);
|
||||
ret = serdev_device_open(serdev);
|
||||
if (ret) {
|
||||
dev_err(&serdev->dev, "Unable to open device\n");
|
||||
goto err_skb;
|
||||
}
|
||||
|
||||
ret = serdev_device_set_baudrate(serdev, 115200);
|
||||
if (ret != 115200) {
|
||||
ret = -EINVAL;
|
||||
goto err_serdev;
|
||||
}
|
||||
|
||||
serdev_device_set_flow_control(serdev, false);
|
||||
|
||||
ret = s3fwrn82_uart_parse_dt(serdev);
|
||||
if (ret < 0)
|
||||
goto err_serdev;
|
||||
|
||||
ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en,
|
||||
GPIOF_OUT_INIT_HIGH, "s3fwrn82_en");
|
||||
if (ret < 0)
|
||||
goto err_serdev;
|
||||
|
||||
ret = devm_gpio_request_one(&phy->ser_dev->dev,
|
||||
phy->common.gpio_fw_wake,
|
||||
GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake");
|
||||
if (ret < 0)
|
||||
goto err_serdev;
|
||||
|
||||
ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev,
|
||||
&uart_phy_ops);
|
||||
if (ret < 0)
|
||||
goto err_serdev;
|
||||
|
||||
return ret;
|
||||
|
||||
err_serdev:
|
||||
serdev_device_close(serdev);
|
||||
err_skb:
|
||||
kfree_skb(phy->recv_skb);
|
||||
err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void s3fwrn82_uart_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
|
||||
|
||||
s3fwrn5_remove(phy->common.ndev);
|
||||
serdev_device_close(serdev);
|
||||
kfree_skb(phy->recv_skb);
|
||||
}
|
||||
|
||||
static struct serdev_device_driver s3fwrn82_uart_driver = {
|
||||
.probe = s3fwrn82_uart_probe,
|
||||
.remove = s3fwrn82_uart_remove,
|
||||
.driver = {
|
||||
.name = "s3fwrn82_uart",
|
||||
.of_match_table = s3fwrn82_uart_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_serdev_device_driver(s3fwrn82_uart_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("UART driver for Samsung NFC");
|
||||
MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");
|
Loading…
x
Reference in New Issue
Block a user