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