1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Generic driver for NXP NCI NFC chips 4 * 5 * Copyright (C) 2014 NXP Semiconductors All rights reserved. 6 * 7 * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> 8 * 9 * Derived from PN544 device driver: 10 * Copyright (C) 2012 Intel Corporation. All rights reserved. 11 */ 12 13 #include <linux/delay.h> 14 #include <linux/gpio.h> 15 #include <linux/module.h> 16 #include <linux/nfc.h> 17 #include <linux/platform_data/nxp-nci.h> 18 19 #include <net/nfc/nci_core.h> 20 21 #include "nxp-nci.h" 22 23 #define NXP_NCI_HDR_LEN 4 24 25 #define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ 26 NFC_PROTO_MIFARE_MASK | \ 27 NFC_PROTO_FELICA_MASK | \ 28 NFC_PROTO_ISO14443_MASK | \ 29 NFC_PROTO_ISO14443_B_MASK | \ 30 NFC_PROTO_NFC_DEP_MASK) 31 32 static int nxp_nci_open(struct nci_dev *ndev) 33 { 34 struct nxp_nci_info *info = nci_get_drvdata(ndev); 35 int r = 0; 36 37 mutex_lock(&info->info_lock); 38 39 if (info->mode != NXP_NCI_MODE_COLD) { 40 r = -EBUSY; 41 goto open_exit; 42 } 43 44 if (info->phy_ops->set_mode) 45 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); 46 47 info->mode = NXP_NCI_MODE_NCI; 48 49 open_exit: 50 mutex_unlock(&info->info_lock); 51 return r; 52 } 53 54 static int nxp_nci_close(struct nci_dev *ndev) 55 { 56 struct nxp_nci_info *info = nci_get_drvdata(ndev); 57 int r = 0; 58 59 mutex_lock(&info->info_lock); 60 61 if (info->phy_ops->set_mode) 62 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 63 64 info->mode = NXP_NCI_MODE_COLD; 65 66 mutex_unlock(&info->info_lock); 67 return r; 68 } 69 70 static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 71 { 72 struct nxp_nci_info *info = nci_get_drvdata(ndev); 73 int r; 74 75 if (!info->phy_ops->write) { 76 r = -ENOTSUPP; 77 goto send_exit; 78 } 79 80 if (info->mode != NXP_NCI_MODE_NCI) { 81 r = -EINVAL; 82 goto send_exit; 83 } 84 85 r = info->phy_ops->write(info->phy_id, skb); 86 if (r < 0) 87 kfree_skb(skb); 88 89 send_exit: 90 return r; 91 } 92 93 static struct nci_ops nxp_nci_ops = { 94 .open = nxp_nci_open, 95 .close = nxp_nci_close, 96 .send = nxp_nci_send, 97 .fw_download = nxp_nci_fw_download, 98 }; 99 100 int nxp_nci_probe(void *phy_id, struct device *pdev, 101 const struct nxp_nci_phy_ops *phy_ops, 102 unsigned int max_payload, 103 struct nci_dev **ndev) 104 { 105 struct nxp_nci_info *info; 106 int r; 107 108 info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); 109 if (!info) { 110 r = -ENOMEM; 111 goto probe_exit; 112 } 113 114 info->phy_id = phy_id; 115 info->pdev = pdev; 116 info->phy_ops = phy_ops; 117 info->max_payload = max_payload; 118 INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); 119 init_completion(&info->fw_info.cmd_completion); 120 mutex_init(&info->info_lock); 121 122 if (info->phy_ops->set_mode) { 123 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 124 if (r < 0) 125 goto probe_exit; 126 } 127 128 info->mode = NXP_NCI_MODE_COLD; 129 130 info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, 131 NXP_NCI_HDR_LEN, 0); 132 if (!info->ndev) { 133 r = -ENOMEM; 134 goto probe_exit; 135 } 136 137 nci_set_parent_dev(info->ndev, pdev); 138 nci_set_drvdata(info->ndev, info); 139 r = nci_register_device(info->ndev); 140 if (r < 0) 141 goto probe_exit_free_nci; 142 143 *ndev = info->ndev; 144 145 goto probe_exit; 146 147 probe_exit_free_nci: 148 nci_free_device(info->ndev); 149 probe_exit: 150 return r; 151 } 152 EXPORT_SYMBOL(nxp_nci_probe); 153 154 void nxp_nci_remove(struct nci_dev *ndev) 155 { 156 struct nxp_nci_info *info = nci_get_drvdata(ndev); 157 158 if (info->mode == NXP_NCI_MODE_FW) 159 nxp_nci_fw_work_complete(info, -ESHUTDOWN); 160 cancel_work_sync(&info->fw_info.work); 161 162 mutex_lock(&info->info_lock); 163 164 if (info->phy_ops->set_mode) 165 info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 166 167 nci_unregister_device(ndev); 168 nci_free_device(ndev); 169 170 mutex_unlock(&info->info_lock); 171 } 172 EXPORT_SYMBOL(nxp_nci_remove); 173 174 MODULE_LICENSE("GPL"); 175 MODULE_DESCRIPTION("NXP NCI NFC driver"); 176 MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>"); 177