From b50e29645627c9372bca45c19d040d96011f77de Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Tue, 17 Nov 2020 17:08:24 +0900 Subject: nfc: s3fwrn5: Remove the max_payload max_payload is unused. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bongsu Jeon Link: https://lore.kernel.org/r/20201117080824epcms2p36f70e06e2d8bd51d1af278b26ca65725@epcms2p3 Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/core.c | 3 +-- drivers/nfc/s3fwrn5/i2c.c | 4 +--- drivers/nfc/s3fwrn5/s3fwrn5.h | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c index ba6c486d6465..f8e5d78d9078 100644 --- a/drivers/nfc/s3fwrn5/core.c +++ b/drivers/nfc/s3fwrn5/core.c @@ -136,7 +136,7 @@ static struct nci_ops s3fwrn5_nci_ops = { }; int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload) + const struct s3fwrn5_phy_ops *phy_ops) { struct s3fwrn5_info *info; int ret; @@ -148,7 +148,6 @@ int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, 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); diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index dc995286be84..0ffa389066a0 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -19,7 +19,6 @@ #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c" -#define S3FWRN5_I2C_MAX_PAYLOAD 32 #define S3FWRN5_EN_WAIT_TIME 150 struct s3fwrn5_i2c_phy { @@ -248,8 +247,7 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, if (ret < 0) return ret; - ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops, - S3FWRN5_I2C_MAX_PAYLOAD); + ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops); if (ret < 0) return ret; diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h index ede68bb5eeae..9d5f34759225 100644 --- a/drivers/nfc/s3fwrn5/s3fwrn5.h +++ b/drivers/nfc/s3fwrn5/s3fwrn5.h @@ -34,7 +34,6 @@ struct s3fwrn5_info { struct device *pdev; const struct s3fwrn5_phy_ops *phy_ops; - unsigned int max_payload; struct s3fwrn5_fw_info fw_info; @@ -79,7 +78,7 @@ static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb) } int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload); + const struct s3fwrn5_phy_ops *phy_ops); void s3fwrn5_remove(struct nci_dev *ndev); int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb, -- cgit v1.2.3 From 074376007e425543ec57ed92bfc3d87f39de3b80 Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Tue, 17 Nov 2020 17:09:50 +0900 Subject: nfc: s3fwrn5: Fix the misspelling in a comment stucture should be replaced by structure. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bongsu Jeon Link: https://lore.kernel.org/r/20201117080950epcms2p4d332688ce351321c3222734bca680b57@epcms2p4 Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/firmware.c b/drivers/nfc/s3fwrn5/firmware.c index ec930ee2c847..4cde6dd5c019 100644 --- a/drivers/nfc/s3fwrn5/firmware.c +++ b/drivers/nfc/s3fwrn5/firmware.c @@ -266,7 +266,7 @@ static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info) } /* - * Firmware header stucture: + * Firmware header structure: * * 0x00 - 0x0B : Date and time string (w/o NUL termination) * 0x10 - 0x13 : Firmware version -- cgit v1.2.3 From e76d795ecb5bb67741fb9e304dcce7950c7aeea0 Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Tue, 17 Nov 2020 17:11:37 +0900 Subject: nfc: s3fwrn5: Change the error code ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bongsu Jeon Link: https://lore.kernel.org/r/20201117081137epcms2p84b5dd8d84ca608f44b0bb722b48f50b1@epcms2p8 Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/s3fwrn5.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h index 9d5f34759225..bb8f936d13a2 100644 --- a/drivers/nfc/s3fwrn5/s3fwrn5.h +++ b/drivers/nfc/s3fwrn5/s3fwrn5.h @@ -44,7 +44,7 @@ static inline int s3fwrn5_set_mode(struct s3fwrn5_info *info, enum s3fwrn5_mode mode) { if (!info->phy_ops->set_mode) - return -ENOTSUPP; + return -EOPNOTSUPP; info->phy_ops->set_mode(info->phy_id, mode); @@ -54,7 +54,7 @@ static inline int s3fwrn5_set_mode(struct s3fwrn5_info *info, static inline enum s3fwrn5_mode s3fwrn5_get_mode(struct s3fwrn5_info *info) { if (!info->phy_ops->get_mode) - return -ENOTSUPP; + return -EOPNOTSUPP; return info->phy_ops->get_mode(info->phy_id); } @@ -62,7 +62,7 @@ static inline enum s3fwrn5_mode s3fwrn5_get_mode(struct s3fwrn5_info *info) static inline int s3fwrn5_set_wake(struct s3fwrn5_info *info, bool wake) { if (!info->phy_ops->set_wake) - return -ENOTSUPP; + return -EOPNOTSUPP; info->phy_ops->set_wake(info->phy_id, wake); @@ -72,7 +72,7 @@ static inline int s3fwrn5_set_wake(struct s3fwrn5_info *info, bool wake) static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb) { if (!info->phy_ops->write) - return -ENOTSUPP; + return -EOPNOTSUPP; return info->phy_ops->write(info->phy_id, skb); } -- cgit v1.2.3 From 337da1499586ed452c1db1e4bbd3106bbafddb6f Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Wed, 2 Dec 2020 20:47:39 +0900 Subject: nfc: s3fwrn5: reduce the EN_WAIT_TIME The delay of 20ms is enough to enable and wake up the Samsung's nfc chip. Acked-by: Krzysztof Kozlowski Signed-off-by: Bongsu Jeon Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index ae26594c7302..9a64eea13506 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -19,7 +19,7 @@ #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c" -#define S3FWRN5_EN_WAIT_TIME 150 +#define S3FWRN5_EN_WAIT_TIME 20 struct s3fwrn5_i2c_phy { struct i2c_client *i2c_dev; @@ -40,7 +40,7 @@ static void s3fwrn5_i2c_set_wake(void *phy_id, bool wake) mutex_lock(&phy->mutex); gpio_set_value(phy->gpio_fw_wake, wake); - msleep(S3FWRN5_EN_WAIT_TIME/2); + msleep(S3FWRN5_EN_WAIT_TIME); mutex_unlock(&phy->mutex); } @@ -63,7 +63,7 @@ static void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode) if (mode != S3FWRN5_MODE_COLD) { msleep(S3FWRN5_EN_WAIT_TIME); gpio_set_value(phy->gpio_en, 0); - msleep(S3FWRN5_EN_WAIT_TIME/2); + msleep(S3FWRN5_EN_WAIT_TIME); } phy->irq_skip = true; -- cgit v1.2.3 From b3799d592f26faaafdfe82fef3f5f91f9764da5f Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Wed, 2 Dec 2020 20:47:40 +0900 Subject: nfc: s3fwrn5: extract the common phy blocks Extract the common phy blocks to reuse it. The UART module will use the common blocks. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bongsu Jeon Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/Makefile | 2 +- drivers/nfc/s3fwrn5/i2c.c | 117 +++++++++++++-------------------------- drivers/nfc/s3fwrn5/phy_common.c | 63 +++++++++++++++++++++ drivers/nfc/s3fwrn5/phy_common.h | 36 ++++++++++++ 4 files changed, 139 insertions(+), 79 deletions(-) create mode 100644 drivers/nfc/s3fwrn5/phy_common.c create mode 100644 drivers/nfc/s3fwrn5/phy_common.h (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/Makefile b/drivers/nfc/s3fwrn5/Makefile index d0ffa35f50e8..6b6f52dfc000 100644 --- a/drivers/nfc/s3fwrn5/Makefile +++ b/drivers/nfc/s3fwrn5/Makefile @@ -3,7 +3,7 @@ # 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 obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5.o diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index 9a64eea13506..e1bdde105f24 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -15,75 +15,30 @@ #include -#include "s3fwrn5.h" +#include "phy_common.h" #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c" -#define S3FWRN5_EN_WAIT_TIME 20 - 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); - 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); - } - 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; } diff --git a/drivers/nfc/s3fwrn5/phy_common.c b/drivers/nfc/s3fwrn5/phy_common.c new file mode 100644 index 000000000000..5cad1f463b48 --- /dev/null +++ b/drivers/nfc/s3fwrn5/phy_common.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Link Layer for Samsung S3FWRN5 NCI based Driver + * + * Copyright (C) 2015 Samsung Electrnoics + * Robert Baldyga + * Copyright (C) 2020 Samsung Electrnoics + * Bongsu Jeon + */ + +#include +#include +#include + +#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); + +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); diff --git a/drivers/nfc/s3fwrn5/phy_common.h b/drivers/nfc/s3fwrn5/phy_common.h new file mode 100644 index 000000000000..b98531decbbc --- /dev/null +++ b/drivers/nfc/s3fwrn5/phy_common.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Link Layer for Samsung S3FWRN5 NCI based Driver + * + * Copyright (C) 2015 Samsung Electrnoics + * Robert Baldyga + * Copyright (C) 2020 Samsung Electrnoics + * Bongsu Jeon + */ + +#ifndef __NFC_S3FWRN5_PHY_COMMON_H +#define __NFC_S3FWRN5_PHY_COMMON_H + +#include +#include + +#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); +enum s3fwrn5_mode s3fwrn5_phy_get_mode(void *phy_id); + +#endif /* __NFC_S3FWRN5_PHY_COMMON_H */ -- cgit v1.2.3 From 3f52c2cb7e3ada37513dabb69a22cf917dba754f Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Wed, 2 Dec 2020 20:47:41 +0900 Subject: nfc: s3fwrn5: Support a UART interface Since S3FWRN82 NFC Chip, The UART interface can be used. S3FWRN82 uses NCI protocol and supports I2C and UART interface. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bongsu Jeon Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/Kconfig | 12 +++ drivers/nfc/s3fwrn5/Makefile | 2 + drivers/nfc/s3fwrn5/phy_common.c | 12 +++ drivers/nfc/s3fwrn5/phy_common.h | 1 + drivers/nfc/s3fwrn5/uart.c | 196 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 drivers/nfc/s3fwrn5/uart.c (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/Kconfig b/drivers/nfc/s3fwrn5/Kconfig index 3f8b6da58280..8a6b1a79de25 100644 --- a/drivers/nfc/s3fwrn5/Kconfig +++ b/drivers/nfc/s3fwrn5/Kconfig @@ -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. diff --git a/drivers/nfc/s3fwrn5/Makefile b/drivers/nfc/s3fwrn5/Makefile index 6b6f52dfc000..7da827ac149c 100644 --- a/drivers/nfc/s3fwrn5/Makefile +++ b/drivers/nfc/s3fwrn5/Makefile @@ -5,6 +5,8 @@ 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 diff --git a/drivers/nfc/s3fwrn5/phy_common.c b/drivers/nfc/s3fwrn5/phy_common.c index 5cad1f463b48..497b02b30ae7 100644 --- a/drivers/nfc/s3fwrn5/phy_common.c +++ b/drivers/nfc/s3fwrn5/phy_common.c @@ -47,6 +47,18 @@ bool s3fwrn5_phy_power_ctrl(struct phy_common *phy, enum s3fwrn5_mode mode) } 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; diff --git a/drivers/nfc/s3fwrn5/phy_common.h b/drivers/nfc/s3fwrn5/phy_common.h index b98531decbbc..99749c9294d1 100644 --- a/drivers/nfc/s3fwrn5/phy_common.h +++ b/drivers/nfc/s3fwrn5/phy_common.h @@ -31,6 +31,7 @@ struct phy_common { 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 */ diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c new file mode 100644 index 000000000000..82ea35d748a5 --- /dev/null +++ b/drivers/nfc/s3fwrn5/uart.c @@ -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 + * Copyright (C) 2020 Samsung Electronics + * Bongsu Jeon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); -- cgit v1.2.3 From 4fb7b98c7be3f7ea55dff36296d51881c6e1ee10 Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Fri, 4 Dec 2020 07:52:57 +0900 Subject: nfc: s3fwrn5: skip the NFC bootloader mode If there isn't a proper NFC firmware image, Bootloader mode will be skipped. Signed-off-by: Bongsu Jeon Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201203225257.2446-1-bongsu.jeon@samsung.com Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/core.c | 23 +++++++++++++++++++++-- drivers/nfc/s3fwrn5/firmware.c | 11 +---------- drivers/nfc/s3fwrn5/firmware.h | 1 + 3 files changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c index f8e5d78d9078..c00b7a07c3ee 100644 --- a/drivers/nfc/s3fwrn5/core.c +++ b/drivers/nfc/s3fwrn5/core.c @@ -20,13 +20,26 @@ NFC_PROTO_ISO14443_B_MASK | \ NFC_PROTO_ISO15693_MASK) +static int s3fwrn5_firmware_init(struct s3fwrn5_info *info) +{ + struct s3fwrn5_fw_info *fw_info = &info->fw_info; + int ret; + + s3fwrn5_fw_init(fw_info, "sec_s3fwrn5_firmware.bin"); + + /* Get firmware data */ + ret = s3fwrn5_fw_request_firmware(fw_info); + if (ret < 0) + dev_err(&fw_info->ndev->nfc_dev->dev, + "Failed to get fw file, ret=%02x\n", ret); + return ret; +} + 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); @@ -109,6 +122,12 @@ static int s3fwrn5_nci_post_setup(struct nci_dev *ndev) struct s3fwrn5_info *info = nci_get_drvdata(ndev); int ret; + if (s3fwrn5_firmware_init(info)) { + //skip bootloader mode + ret = 0; + goto out; + } + ret = s3fwrn5_firmware_update(info); if (ret < 0) goto out; diff --git a/drivers/nfc/s3fwrn5/firmware.c b/drivers/nfc/s3fwrn5/firmware.c index 4cde6dd5c019..4b5352e2b915 100644 --- a/drivers/nfc/s3fwrn5/firmware.c +++ b/drivers/nfc/s3fwrn5/firmware.c @@ -280,7 +280,7 @@ static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info) #define S3FWRN5_FW_IMAGE_HEADER_SIZE 44 -static int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info) +int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info) { struct s3fwrn5_fw_image *fw = &fw_info->fw; u32 sig_off; @@ -358,15 +358,6 @@ int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info) struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo; int ret; - /* Get firmware data */ - - ret = s3fwrn5_fw_request_firmware(fw_info); - if (ret < 0) { - dev_err(&fw_info->ndev->nfc_dev->dev, - "Failed to get fw file, ret=%02x\n", ret); - return ret; - } - /* Get bootloader info */ ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo); diff --git a/drivers/nfc/s3fwrn5/firmware.h b/drivers/nfc/s3fwrn5/firmware.h index 3c83e6730d30..3a82ce5837fb 100644 --- a/drivers/nfc/s3fwrn5/firmware.h +++ b/drivers/nfc/s3fwrn5/firmware.h @@ -89,6 +89,7 @@ struct s3fwrn5_fw_info { char parity; }; +int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info); void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name); int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info); bool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version); -- cgit v1.2.3 From 237410fb83538407646109063c4d902c42261387 Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Tue, 8 Dec 2020 23:10:12 +0900 Subject: nfc: s3fwrn5: Remove hard coded interrupt trigger type from the i2c module For the flexible control of interrupt trigger type, remove the hard coded interrupt trigger type in the i2c module. The trigger type will be loaded from a dts. Signed-off-by: Bongsu Jeon Signed-off-by: David S. Miller --- drivers/nfc/s3fwrn5/i2c.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index e1bdde105f24..42f1f610ac2c 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -179,6 +179,8 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct s3fwrn5_i2c_phy *phy; + struct irq_data *irq_data; + unsigned long irqflags; int ret; phy = devm_kzalloc(&client->dev, sizeof(*phy), GFP_KERNEL); @@ -212,8 +214,11 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, if (ret < 0) return ret; + irq_data = irq_get_irq_data(client->irq); + irqflags = irqd_get_trigger_type(irq_data) | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL, - s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + s3fwrn5_i2c_irq_thread_fn, irqflags, S3FWRN5_I2C_DRIVER_NAME, phy); if (ret) s3fwrn5_remove(phy->common.ndev); -- cgit v1.2.3 From e0a64d1dffca048a99546993322bd1fb5c728ee8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 10 Dec 2020 22:18:24 +0100 Subject: nfc: s3fwrn5: let core configure the interrupt trigger If interrupt trigger is not set when requesting the interrupt, the core will take care of reading trigger type from Devicetree. There is no point to do it in the driver. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201210211824.214949-1-krzk@kernel.org Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/i2c.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index 42f1f610ac2c..897394167522 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -179,8 +179,6 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct s3fwrn5_i2c_phy *phy; - struct irq_data *irq_data; - unsigned long irqflags; int ret; phy = devm_kzalloc(&client->dev, sizeof(*phy), GFP_KERNEL); @@ -214,11 +212,8 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, if (ret < 0) return ret; - irq_data = irq_get_irq_data(client->irq); - irqflags = irqd_get_trigger_type(irq_data) | IRQF_ONESHOT; - ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL, - s3fwrn5_i2c_irq_thread_fn, irqflags, + s3fwrn5_i2c_irq_thread_fn, IRQF_ONESHOT, S3FWRN5_I2C_DRIVER_NAME, phy); if (ret) s3fwrn5_remove(phy->common.ndev); -- cgit v1.2.3 From a4485baefa1efa596702ebffd5a9c760d42b14b5 Mon Sep 17 00:00:00 2001 From: Bongsu Jeon Date: Sun, 13 Dec 2020 18:58:50 +0900 Subject: nfc: s3fwrn5: Release the nfc firmware add the code to release the nfc firmware when the firmware image size is wrong. Fixes: c04c674fadeb ("nfc: s3fwrn5: Add driver for Samsung S3FWRN5 NFC Chip") Signed-off-by: Bongsu Jeon Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201213095850.28169-1-bongsu.jeon@samsung.com Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/firmware.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/nfc/s3fwrn5') diff --git a/drivers/nfc/s3fwrn5/firmware.c b/drivers/nfc/s3fwrn5/firmware.c index 4b5352e2b915..b70737b3070e 100644 --- a/drivers/nfc/s3fwrn5/firmware.c +++ b/drivers/nfc/s3fwrn5/firmware.c @@ -293,8 +293,10 @@ int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info) if (ret < 0) return ret; - if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) + if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) { + release_firmware(fw->fw); return -EINVAL; + } memcpy(fw->date, fw->fw->data + 0x00, 12); fw->date[12] = '\0'; -- cgit v1.2.3