Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * NCI based driver for Samsung S3FWRN5 NFC chip
0004  *
0005  * Copyright (C) 2015 Samsung Electrnoics
0006  * Robert Baldyga <r.baldyga@samsung.com>
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <net/nfc/nci_core.h>
0011 
0012 #include "s3fwrn5.h"
0013 #include "firmware.h"
0014 #include "nci.h"
0015 
0016 #define S3FWRN5_NFC_PROTOCOLS  (NFC_PROTO_JEWEL_MASK | \
0017                 NFC_PROTO_MIFARE_MASK | \
0018                 NFC_PROTO_FELICA_MASK | \
0019                 NFC_PROTO_ISO14443_MASK | \
0020                 NFC_PROTO_ISO14443_B_MASK | \
0021                 NFC_PROTO_ISO15693_MASK)
0022 
0023 static int s3fwrn5_firmware_init(struct s3fwrn5_info *info)
0024 {
0025     struct s3fwrn5_fw_info *fw_info = &info->fw_info;
0026     int ret;
0027 
0028     s3fwrn5_fw_init(fw_info, "sec_s3fwrn5_firmware.bin");
0029 
0030     /* Get firmware data */
0031     ret = s3fwrn5_fw_request_firmware(fw_info);
0032     if (ret < 0)
0033         dev_err(&fw_info->ndev->nfc_dev->dev,
0034             "Failed to get fw file, ret=%02x\n", ret);
0035     return ret;
0036 }
0037 
0038 static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
0039 {
0040     bool need_update;
0041     int ret;
0042 
0043     /* Update firmware */
0044 
0045     s3fwrn5_set_wake(info, false);
0046     s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);
0047 
0048     ret = s3fwrn5_fw_setup(&info->fw_info);
0049     if (ret < 0)
0050         return ret;
0051 
0052     need_update = s3fwrn5_fw_check_version(&info->fw_info,
0053         info->ndev->manufact_specific_info);
0054     if (!need_update)
0055         goto out;
0056 
0057     dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");
0058 
0059     ret = s3fwrn5_fw_download(&info->fw_info);
0060     if (ret < 0)
0061         goto out;
0062 
0063     /* Update RF configuration */
0064 
0065     s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
0066 
0067     s3fwrn5_set_wake(info, true);
0068     ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
0069     s3fwrn5_set_wake(info, false);
0070 
0071 out:
0072     s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
0073     s3fwrn5_fw_cleanup(&info->fw_info);
0074     return ret;
0075 }
0076 
0077 static int s3fwrn5_nci_open(struct nci_dev *ndev)
0078 {
0079     struct s3fwrn5_info *info = nci_get_drvdata(ndev);
0080 
0081     if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
0082         return  -EBUSY;
0083 
0084     s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
0085     s3fwrn5_set_wake(info, true);
0086 
0087     return 0;
0088 }
0089 
0090 static int s3fwrn5_nci_close(struct nci_dev *ndev)
0091 {
0092     struct s3fwrn5_info *info = nci_get_drvdata(ndev);
0093 
0094     s3fwrn5_set_wake(info, false);
0095     s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
0096 
0097     return 0;
0098 }
0099 
0100 static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
0101 {
0102     struct s3fwrn5_info *info = nci_get_drvdata(ndev);
0103     int ret;
0104 
0105     mutex_lock(&info->mutex);
0106 
0107     if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
0108         mutex_unlock(&info->mutex);
0109         return -EINVAL;
0110     }
0111 
0112     ret = s3fwrn5_write(info, skb);
0113     if (ret < 0)
0114         kfree_skb(skb);
0115 
0116     mutex_unlock(&info->mutex);
0117     return ret;
0118 }
0119 
0120 static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
0121 {
0122     struct s3fwrn5_info *info = nci_get_drvdata(ndev);
0123     int ret;
0124 
0125     if (s3fwrn5_firmware_init(info)) {
0126         //skip bootloader mode
0127         return 0;
0128     }
0129 
0130     ret = s3fwrn5_firmware_update(info);
0131     if (ret < 0)
0132         return ret;
0133 
0134     /* NCI core reset */
0135 
0136     s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
0137     s3fwrn5_set_wake(info, true);
0138 
0139     ret = nci_core_reset(info->ndev);
0140     if (ret < 0)
0141         return ret;
0142 
0143     return nci_core_init(info->ndev);
0144 }
0145 
0146 static const struct nci_ops s3fwrn5_nci_ops = {
0147     .open = s3fwrn5_nci_open,
0148     .close = s3fwrn5_nci_close,
0149     .send = s3fwrn5_nci_send,
0150     .post_setup = s3fwrn5_nci_post_setup,
0151     .prop_ops = s3fwrn5_nci_prop_ops,
0152     .n_prop_ops = ARRAY_SIZE(s3fwrn5_nci_prop_ops),
0153 };
0154 
0155 int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
0156     const struct s3fwrn5_phy_ops *phy_ops)
0157 {
0158     struct s3fwrn5_info *info;
0159     int ret;
0160 
0161     info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
0162     if (!info)
0163         return -ENOMEM;
0164 
0165     info->phy_id = phy_id;
0166     info->pdev = pdev;
0167     info->phy_ops = phy_ops;
0168     mutex_init(&info->mutex);
0169 
0170     s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
0171 
0172     info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
0173         S3FWRN5_NFC_PROTOCOLS, 0, 0);
0174     if (!info->ndev)
0175         return -ENOMEM;
0176 
0177     nci_set_parent_dev(info->ndev, pdev);
0178     nci_set_drvdata(info->ndev, info);
0179 
0180     ret = nci_register_device(info->ndev);
0181     if (ret < 0) {
0182         nci_free_device(info->ndev);
0183         return ret;
0184     }
0185 
0186     info->fw_info.ndev = info->ndev;
0187 
0188     *ndev = info->ndev;
0189 
0190     return ret;
0191 }
0192 EXPORT_SYMBOL(s3fwrn5_probe);
0193 
0194 void s3fwrn5_remove(struct nci_dev *ndev)
0195 {
0196     struct s3fwrn5_info *info = nci_get_drvdata(ndev);
0197 
0198     s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
0199 
0200     nci_unregister_device(ndev);
0201     nci_free_device(ndev);
0202 }
0203 EXPORT_SYMBOL(s3fwrn5_remove);
0204 
0205 int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
0206     enum s3fwrn5_mode mode)
0207 {
0208     switch (mode) {
0209     case S3FWRN5_MODE_NCI:
0210         return nci_recv_frame(ndev, skb);
0211     case S3FWRN5_MODE_FW:
0212         return s3fwrn5_fw_recv_frame(ndev, skb);
0213     default:
0214         kfree_skb(skb);
0215         return -ENODEV;
0216     }
0217 }
0218 EXPORT_SYMBOL(s3fwrn5_recv_frame);
0219 
0220 MODULE_LICENSE("GPL");
0221 MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
0222 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");