162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic driver for NXP NCI NFC chips 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 NXP Semiconductors All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Derived from PN544 device driver: 1062306a36Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/nfc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <net/nfc/nci_core.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "nxp-nci.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define NXP_NCI_HDR_LEN 4 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ 2462306a36Sopenharmony_ci NFC_PROTO_MIFARE_MASK | \ 2562306a36Sopenharmony_ci NFC_PROTO_FELICA_MASK | \ 2662306a36Sopenharmony_ci NFC_PROTO_ISO14443_MASK | \ 2762306a36Sopenharmony_ci NFC_PROTO_ISO14443_B_MASK | \ 2862306a36Sopenharmony_ci NFC_PROTO_NFC_DEP_MASK) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define NXP_NCI_RF_PLL_UNLOCKED_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x21) 3162306a36Sopenharmony_ci#define NXP_NCI_RF_TXLDO_ERROR_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x23) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int nxp_nci_open(struct nci_dev *ndev) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct nxp_nci_info *info = nci_get_drvdata(ndev); 3662306a36Sopenharmony_ci int r = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci mutex_lock(&info->info_lock); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (info->mode != NXP_NCI_MODE_COLD) { 4162306a36Sopenharmony_ci r = -EBUSY; 4262306a36Sopenharmony_ci goto open_exit; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (info->phy_ops->set_mode) 4662306a36Sopenharmony_ci r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci info->mode = NXP_NCI_MODE_NCI; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciopen_exit: 5162306a36Sopenharmony_ci mutex_unlock(&info->info_lock); 5262306a36Sopenharmony_ci return r; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int nxp_nci_close(struct nci_dev *ndev) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct nxp_nci_info *info = nci_get_drvdata(ndev); 5862306a36Sopenharmony_ci int r = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci mutex_lock(&info->info_lock); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (info->phy_ops->set_mode) 6362306a36Sopenharmony_ci r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci info->mode = NXP_NCI_MODE_COLD; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci mutex_unlock(&info->info_lock); 6862306a36Sopenharmony_ci return r; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct nxp_nci_info *info = nci_get_drvdata(ndev); 7462306a36Sopenharmony_ci int r; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (!info->phy_ops->write) { 7762306a36Sopenharmony_ci kfree_skb(skb); 7862306a36Sopenharmony_ci return -EOPNOTSUPP; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (info->mode != NXP_NCI_MODE_NCI) { 8262306a36Sopenharmony_ci kfree_skb(skb); 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci r = info->phy_ops->write(info->phy_id, skb); 8762306a36Sopenharmony_ci if (r < 0) { 8862306a36Sopenharmony_ci kfree_skb(skb); 8962306a36Sopenharmony_ci return r; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci consume_skb(skb); 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int nxp_nci_rf_pll_unlocked_ntf(struct nci_dev *ndev, 9762306a36Sopenharmony_ci struct sk_buff *skb) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci nfc_err(&ndev->nfc_dev->dev, 10062306a36Sopenharmony_ci "PLL didn't lock. Missing or unstable clock?\n"); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int nxp_nci_rf_txldo_error_ntf(struct nci_dev *ndev, 10662306a36Sopenharmony_ci struct sk_buff *skb) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci nfc_err(&ndev->nfc_dev->dev, 10962306a36Sopenharmony_ci "RF transmitter couldn't start. Bad power and/or configuration?\n"); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const struct nci_driver_ops nxp_nci_core_ops[] = { 11562306a36Sopenharmony_ci { 11662306a36Sopenharmony_ci .opcode = NXP_NCI_RF_PLL_UNLOCKED_NTF, 11762306a36Sopenharmony_ci .ntf = nxp_nci_rf_pll_unlocked_ntf, 11862306a36Sopenharmony_ci }, 11962306a36Sopenharmony_ci { 12062306a36Sopenharmony_ci .opcode = NXP_NCI_RF_TXLDO_ERROR_NTF, 12162306a36Sopenharmony_ci .ntf = nxp_nci_rf_txldo_error_ntf, 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct nci_ops nxp_nci_ops = { 12662306a36Sopenharmony_ci .open = nxp_nci_open, 12762306a36Sopenharmony_ci .close = nxp_nci_close, 12862306a36Sopenharmony_ci .send = nxp_nci_send, 12962306a36Sopenharmony_ci .fw_download = nxp_nci_fw_download, 13062306a36Sopenharmony_ci .core_ops = nxp_nci_core_ops, 13162306a36Sopenharmony_ci .n_core_ops = ARRAY_SIZE(nxp_nci_core_ops), 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciint nxp_nci_probe(void *phy_id, struct device *pdev, 13562306a36Sopenharmony_ci const struct nxp_nci_phy_ops *phy_ops, 13662306a36Sopenharmony_ci unsigned int max_payload, 13762306a36Sopenharmony_ci struct nci_dev **ndev) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct nxp_nci_info *info; 14062306a36Sopenharmony_ci int r; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!info) 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci info->phy_id = phy_id; 14762306a36Sopenharmony_ci info->pdev = pdev; 14862306a36Sopenharmony_ci info->phy_ops = phy_ops; 14962306a36Sopenharmony_ci info->max_payload = max_payload; 15062306a36Sopenharmony_ci INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); 15162306a36Sopenharmony_ci init_completion(&info->fw_info.cmd_completion); 15262306a36Sopenharmony_ci mutex_init(&info->info_lock); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (info->phy_ops->set_mode) { 15562306a36Sopenharmony_ci r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 15662306a36Sopenharmony_ci if (r < 0) 15762306a36Sopenharmony_ci return r; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci info->mode = NXP_NCI_MODE_COLD; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, 16362306a36Sopenharmony_ci NXP_NCI_HDR_LEN, 0); 16462306a36Sopenharmony_ci if (!info->ndev) 16562306a36Sopenharmony_ci return -ENOMEM; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci nci_set_parent_dev(info->ndev, pdev); 16862306a36Sopenharmony_ci nci_set_drvdata(info->ndev, info); 16962306a36Sopenharmony_ci r = nci_register_device(info->ndev); 17062306a36Sopenharmony_ci if (r < 0) { 17162306a36Sopenharmony_ci nci_free_device(info->ndev); 17262306a36Sopenharmony_ci return r; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci *ndev = info->ndev; 17662306a36Sopenharmony_ci return r; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ciEXPORT_SYMBOL(nxp_nci_probe); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_civoid nxp_nci_remove(struct nci_dev *ndev) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct nxp_nci_info *info = nci_get_drvdata(ndev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (info->mode == NXP_NCI_MODE_FW) 18562306a36Sopenharmony_ci nxp_nci_fw_work_complete(info, -ESHUTDOWN); 18662306a36Sopenharmony_ci cancel_work_sync(&info->fw_info.work); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mutex_lock(&info->info_lock); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (info->phy_ops->set_mode) 19162306a36Sopenharmony_ci info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci nci_unregister_device(ndev); 19462306a36Sopenharmony_ci nci_free_device(ndev); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci mutex_unlock(&info->info_lock); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL(nxp_nci_remove); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 20162306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP NCI NFC driver"); 20262306a36Sopenharmony_ciMODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>"); 203