1/* 2 * Generic driver for NXP NCI NFC chips 3 * 4 * Copyright (C) 2014 NXP Semiconductors All rights reserved. 5 * 6 * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> 7 * 8 * Derived from PN544 device driver: 9 * Copyright (C) 2012 Intel Corporation. All rights reserved. 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms and conditions of the GNU General Public License, 13 * version 2, as published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, see <http://www.gnu.org/licenses/>. 22 */ 23 24#include <linux/delay.h> 25#include <linux/gpio.h> 26#include <linux/module.h> 27#include <linux/nfc.h> 28#include <linux/platform_data/nxp-nci.h> 29 30#include <net/nfc/nci_core.h> 31 32#include "nxp-nci.h" 33 34#define NXP_NCI_HDR_LEN 4 35 36#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ 37 NFC_PROTO_MIFARE_MASK | \ 38 NFC_PROTO_FELICA_MASK | \ 39 NFC_PROTO_ISO14443_MASK | \ 40 NFC_PROTO_ISO14443_B_MASK | \ 41 NFC_PROTO_NFC_DEP_MASK) 42 43static int nxp_nci_open(struct nci_dev *ndev) 44{ 45 struct nxp_nci_info *info = nci_get_drvdata(ndev); 46 int r = 0; 47 48 mutex_lock(&info->info_lock); 49 50 if (info->mode != NXP_NCI_MODE_COLD) { 51 r = -EBUSY; 52 goto open_exit; 53 } 54 55 if (info->phy_ops->set_mode) 56 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); 57 58 info->mode = NXP_NCI_MODE_NCI; 59 60open_exit: 61 mutex_unlock(&info->info_lock); 62 return r; 63} 64 65static int nxp_nci_close(struct nci_dev *ndev) 66{ 67 struct nxp_nci_info *info = nci_get_drvdata(ndev); 68 int r = 0; 69 70 mutex_lock(&info->info_lock); 71 72 if (info->phy_ops->set_mode) 73 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 74 75 info->mode = NXP_NCI_MODE_COLD; 76 77 mutex_unlock(&info->info_lock); 78 return r; 79} 80 81static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 82{ 83 struct nxp_nci_info *info = nci_get_drvdata(ndev); 84 int r; 85 86 if (!info->phy_ops->write) { 87 r = -ENOTSUPP; 88 goto send_exit; 89 } 90 91 if (info->mode != NXP_NCI_MODE_NCI) { 92 r = -EINVAL; 93 goto send_exit; 94 } 95 96 r = info->phy_ops->write(info->phy_id, skb); 97 if (r < 0) 98 kfree_skb(skb); 99 100send_exit: 101 return r; 102} 103 104static struct nci_ops nxp_nci_ops = { 105 .open = nxp_nci_open, 106 .close = nxp_nci_close, 107 .send = nxp_nci_send, 108 .fw_download = nxp_nci_fw_download, 109}; 110 111int nxp_nci_probe(void *phy_id, struct device *pdev, 112 struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, 113 struct nci_dev **ndev) 114{ 115 struct nxp_nci_info *info; 116 int r; 117 118 info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); 119 if (!info) { 120 r = -ENOMEM; 121 goto probe_exit; 122 } 123 124 info->phy_id = phy_id; 125 info->pdev = pdev; 126 info->phy_ops = phy_ops; 127 info->max_payload = max_payload; 128 INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); 129 init_completion(&info->fw_info.cmd_completion); 130 mutex_init(&info->info_lock); 131 132 if (info->phy_ops->set_mode) { 133 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 134 if (r < 0) 135 goto probe_exit; 136 } 137 138 info->mode = NXP_NCI_MODE_COLD; 139 140 info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, 141 NXP_NCI_HDR_LEN, 0); 142 if (!info->ndev) { 143 r = -ENOMEM; 144 goto probe_exit; 145 } 146 147 nci_set_parent_dev(info->ndev, pdev); 148 nci_set_drvdata(info->ndev, info); 149 r = nci_register_device(info->ndev); 150 if (r < 0) 151 goto probe_exit_free_nci; 152 153 *ndev = info->ndev; 154 155 goto probe_exit; 156 157probe_exit_free_nci: 158 nci_free_device(info->ndev); 159probe_exit: 160 return r; 161} 162EXPORT_SYMBOL(nxp_nci_probe); 163 164void nxp_nci_remove(struct nci_dev *ndev) 165{ 166 struct nxp_nci_info *info = nci_get_drvdata(ndev); 167 168 if (info->mode == NXP_NCI_MODE_FW) 169 nxp_nci_fw_work_complete(info, -ESHUTDOWN); 170 cancel_work_sync(&info->fw_info.work); 171 172 mutex_lock(&info->info_lock); 173 174 if (info->phy_ops->set_mode) 175 info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 176 177 nci_unregister_device(ndev); 178 nci_free_device(ndev); 179 180 mutex_unlock(&info->info_lock); 181} 182EXPORT_SYMBOL(nxp_nci_remove); 183 184MODULE_LICENSE("GPL"); 185MODULE_DESCRIPTION("NXP NCI NFC driver"); 186MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>"); 187