core.c (3589B)
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 83 return r; 84} 85 86static const struct nci_ops nxp_nci_ops = { 87 .open = nxp_nci_open, 88 .close = nxp_nci_close, 89 .send = nxp_nci_send, 90 .fw_download = nxp_nci_fw_download, 91}; 92 93int nxp_nci_probe(void *phy_id, struct device *pdev, 94 const struct nxp_nci_phy_ops *phy_ops, 95 unsigned int max_payload, 96 struct nci_dev **ndev) 97{ 98 struct nxp_nci_info *info; 99 int r; 100 101 info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); 102 if (!info) 103 return -ENOMEM; 104 105 info->phy_id = phy_id; 106 info->pdev = pdev; 107 info->phy_ops = phy_ops; 108 info->max_payload = max_payload; 109 INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); 110 init_completion(&info->fw_info.cmd_completion); 111 mutex_init(&info->info_lock); 112 113 if (info->phy_ops->set_mode) { 114 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 115 if (r < 0) 116 return r; 117 } 118 119 info->mode = NXP_NCI_MODE_COLD; 120 121 info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, 122 NXP_NCI_HDR_LEN, 0); 123 if (!info->ndev) 124 return -ENOMEM; 125 126 nci_set_parent_dev(info->ndev, pdev); 127 nci_set_drvdata(info->ndev, info); 128 r = nci_register_device(info->ndev); 129 if (r < 0) { 130 nci_free_device(info->ndev); 131 return r; 132 } 133 134 *ndev = info->ndev; 135 return r; 136} 137EXPORT_SYMBOL(nxp_nci_probe); 138 139void nxp_nci_remove(struct nci_dev *ndev) 140{ 141 struct nxp_nci_info *info = nci_get_drvdata(ndev); 142 143 if (info->mode == NXP_NCI_MODE_FW) 144 nxp_nci_fw_work_complete(info, -ESHUTDOWN); 145 cancel_work_sync(&info->fw_info.work); 146 147 mutex_lock(&info->info_lock); 148 149 if (info->phy_ops->set_mode) 150 info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 151 152 nci_unregister_device(ndev); 153 nci_free_device(ndev); 154 155 mutex_unlock(&info->info_lock); 156} 157EXPORT_SYMBOL(nxp_nci_remove); 158 159MODULE_LICENSE("GPL"); 160MODULE_DESCRIPTION("NXP NCI NFC driver"); 161MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");