linux/drivers/nfc/s3fwrn5/core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * NCI based driver for Samsung S3FWRN5 NFC chip
   4 *
   5 * Copyright (C) 2015 Samsung Electrnoics
   6 * Robert Baldyga <r.baldyga@samsung.com>
   7 */
   8
   9#include <linux/module.h>
  10#include <net/nfc/nci_core.h>
  11
  12#include "s3fwrn5.h"
  13#include "firmware.h"
  14#include "nci.h"
  15
  16#define S3FWRN5_NFC_PROTOCOLS  (NFC_PROTO_JEWEL_MASK | \
  17                                NFC_PROTO_MIFARE_MASK | \
  18                                NFC_PROTO_FELICA_MASK | \
  19                                NFC_PROTO_ISO14443_MASK | \
  20                                NFC_PROTO_ISO14443_B_MASK | \
  21                                NFC_PROTO_ISO15693_MASK)
  22
  23static int s3fwrn5_firmware_init(struct s3fwrn5_info *info)
  24{
  25        struct s3fwrn5_fw_info *fw_info = &info->fw_info;
  26        int ret;
  27
  28        s3fwrn5_fw_init(fw_info, "sec_s3fwrn5_firmware.bin");
  29
  30        /* Get firmware data */
  31        ret = s3fwrn5_fw_request_firmware(fw_info);
  32        if (ret < 0)
  33                dev_err(&fw_info->ndev->nfc_dev->dev,
  34                        "Failed to get fw file, ret=%02x\n", ret);
  35        return ret;
  36}
  37
  38static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
  39{
  40        bool need_update;
  41        int ret;
  42
  43        /* Update firmware */
  44
  45        s3fwrn5_set_wake(info, false);
  46        s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);
  47
  48        ret = s3fwrn5_fw_setup(&info->fw_info);
  49        if (ret < 0)
  50                return ret;
  51
  52        need_update = s3fwrn5_fw_check_version(&info->fw_info,
  53                info->ndev->manufact_specific_info);
  54        if (!need_update)
  55                goto out;
  56
  57        dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");
  58
  59        ret = s3fwrn5_fw_download(&info->fw_info);
  60        if (ret < 0)
  61                goto out;
  62
  63        /* Update RF configuration */
  64
  65        s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
  66
  67        s3fwrn5_set_wake(info, true);
  68        ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
  69        s3fwrn5_set_wake(info, false);
  70
  71out:
  72        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
  73        s3fwrn5_fw_cleanup(&info->fw_info);
  74        return ret;
  75}
  76
  77static int s3fwrn5_nci_open(struct nci_dev *ndev)
  78{
  79        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
  80
  81        if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
  82                return  -EBUSY;
  83
  84        s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
  85        s3fwrn5_set_wake(info, true);
  86
  87        return 0;
  88}
  89
  90static int s3fwrn5_nci_close(struct nci_dev *ndev)
  91{
  92        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
  93
  94        s3fwrn5_set_wake(info, false);
  95        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
  96
  97        return 0;
  98}
  99
 100static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
 101{
 102        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
 103        int ret;
 104
 105        mutex_lock(&info->mutex);
 106
 107        if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
 108                mutex_unlock(&info->mutex);
 109                return -EINVAL;
 110        }
 111
 112        ret = s3fwrn5_write(info, skb);
 113        if (ret < 0)
 114                kfree_skb(skb);
 115
 116        mutex_unlock(&info->mutex);
 117        return ret;
 118}
 119
 120static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
 121{
 122        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
 123        int ret;
 124
 125        if (s3fwrn5_firmware_init(info)) {
 126                //skip bootloader mode
 127                return 0;
 128        }
 129
 130        ret = s3fwrn5_firmware_update(info);
 131        if (ret < 0)
 132                return ret;
 133
 134        /* NCI core reset */
 135
 136        s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
 137        s3fwrn5_set_wake(info, true);
 138
 139        ret = nci_core_reset(info->ndev);
 140        if (ret < 0)
 141                return ret;
 142
 143        return nci_core_init(info->ndev);
 144}
 145
 146static struct nci_ops s3fwrn5_nci_ops = {
 147        .open = s3fwrn5_nci_open,
 148        .close = s3fwrn5_nci_close,
 149        .send = s3fwrn5_nci_send,
 150        .post_setup = s3fwrn5_nci_post_setup,
 151};
 152
 153int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
 154        const struct s3fwrn5_phy_ops *phy_ops)
 155{
 156        struct s3fwrn5_info *info;
 157        int ret;
 158
 159        info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
 160        if (!info)
 161                return -ENOMEM;
 162
 163        info->phy_id = phy_id;
 164        info->pdev = pdev;
 165        info->phy_ops = phy_ops;
 166        mutex_init(&info->mutex);
 167
 168        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
 169
 170        s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
 171                &s3fwrn5_nci_ops.n_prop_ops);
 172
 173        info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
 174                S3FWRN5_NFC_PROTOCOLS, 0, 0);
 175        if (!info->ndev)
 176                return -ENOMEM;
 177
 178        nci_set_parent_dev(info->ndev, pdev);
 179        nci_set_drvdata(info->ndev, info);
 180
 181        ret = nci_register_device(info->ndev);
 182        if (ret < 0) {
 183                nci_free_device(info->ndev);
 184                return ret;
 185        }
 186
 187        info->fw_info.ndev = info->ndev;
 188
 189        *ndev = info->ndev;
 190
 191        return ret;
 192}
 193EXPORT_SYMBOL(s3fwrn5_probe);
 194
 195void s3fwrn5_remove(struct nci_dev *ndev)
 196{
 197        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
 198
 199        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
 200
 201        nci_unregister_device(ndev);
 202        nci_free_device(ndev);
 203}
 204EXPORT_SYMBOL(s3fwrn5_remove);
 205
 206int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
 207        enum s3fwrn5_mode mode)
 208{
 209        switch (mode) {
 210        case S3FWRN5_MODE_NCI:
 211                return nci_recv_frame(ndev, skb);
 212        case S3FWRN5_MODE_FW:
 213                return s3fwrn5_fw_recv_frame(ndev, skb);
 214        default:
 215                kfree_skb(skb);
 216                return -ENODEV;
 217        }
 218}
 219EXPORT_SYMBOL(s3fwrn5_recv_frame);
 220
 221MODULE_LICENSE("GPL");
 222MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
 223MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
 224