2019-05-31 01:09:57 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-03-09 11:12:04 +01:00
/*
* Generic driver for NXP NCI NFC chips
*
* Copyright ( C ) 2014 NXP Semiconductors All rights reserved .
*
* Authors : Clément Perrochaud < clement . perrochaud @ nxp . com >
*
* Derived from PN544 device driver :
* Copyright ( C ) 2012 Intel Corporation . All rights reserved .
*/
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/nfc.h>
# include <net/nfc/nci_core.h>
# include "nxp-nci.h"
# define NXP_NCI_HDR_LEN 4
# define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
NFC_PROTO_MIFARE_MASK | \
NFC_PROTO_FELICA_MASK | \
NFC_PROTO_ISO14443_MASK | \
NFC_PROTO_ISO14443_B_MASK | \
NFC_PROTO_NFC_DEP_MASK )
2022-07-12 19:00:10 +02:00
# define NXP_NCI_RF_PLL_UNLOCKED_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x21)
# define NXP_NCI_RF_TXLDO_ERROR_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x23)
2015-03-09 11:12:04 +01:00
static int nxp_nci_open ( struct nci_dev * ndev )
{
struct nxp_nci_info * info = nci_get_drvdata ( ndev ) ;
int r = 0 ;
mutex_lock ( & info - > info_lock ) ;
if ( info - > mode ! = NXP_NCI_MODE_COLD ) {
r = - EBUSY ;
goto open_exit ;
}
if ( info - > phy_ops - > set_mode )
r = info - > phy_ops - > set_mode ( info - > phy_id , NXP_NCI_MODE_NCI ) ;
info - > mode = NXP_NCI_MODE_NCI ;
open_exit :
mutex_unlock ( & info - > info_lock ) ;
return r ;
}
static int nxp_nci_close ( struct nci_dev * ndev )
{
struct nxp_nci_info * info = nci_get_drvdata ( ndev ) ;
int r = 0 ;
mutex_lock ( & info - > info_lock ) ;
if ( info - > phy_ops - > set_mode )
r = info - > phy_ops - > set_mode ( info - > phy_id , NXP_NCI_MODE_COLD ) ;
info - > mode = NXP_NCI_MODE_COLD ;
mutex_unlock ( & info - > info_lock ) ;
return r ;
}
static int nxp_nci_send ( struct nci_dev * ndev , struct sk_buff * skb )
{
struct nxp_nci_info * info = nci_get_drvdata ( ndev ) ;
int r ;
2022-11-17 19:37:13 +08:00
if ( ! info - > phy_ops - > write ) {
kfree_skb ( skb ) ;
2021-06-18 17:10:16 +08:00
return - EOPNOTSUPP ;
2022-11-17 19:37:13 +08:00
}
2015-03-09 11:12:04 +01:00
2022-11-17 19:37:13 +08:00
if ( info - > mode ! = NXP_NCI_MODE_NCI ) {
kfree_skb ( skb ) ;
2021-06-18 17:10:16 +08:00
return - EINVAL ;
2022-11-17 19:37:13 +08:00
}
2015-03-09 11:12:04 +01:00
r = info - > phy_ops - > write ( info - > phy_id , skb ) ;
2022-10-27 22:03:30 +08:00
if ( r < 0 ) {
2015-03-09 11:12:04 +01:00
kfree_skb ( skb ) ;
2022-10-27 22:03:30 +08:00
return r ;
}
2015-03-09 11:12:04 +01:00
2022-10-27 22:03:30 +08:00
consume_skb ( skb ) ;
return 0 ;
2015-03-09 11:12:04 +01:00
}
2022-07-12 19:00:10 +02:00
static int nxp_nci_rf_pll_unlocked_ntf ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
nfc_err ( & ndev - > nfc_dev - > dev ,
" PLL didn't lock. Missing or unstable clock? \n " ) ;
return 0 ;
}
static int nxp_nci_rf_txldo_error_ntf ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
nfc_err ( & ndev - > nfc_dev - > dev ,
" RF transmitter couldn't start. Bad power and/or configuration? \n " ) ;
return 0 ;
}
static const struct nci_driver_ops nxp_nci_core_ops [ ] = {
{
. opcode = NXP_NCI_RF_PLL_UNLOCKED_NTF ,
. ntf = nxp_nci_rf_pll_unlocked_ntf ,
} ,
{
. opcode = NXP_NCI_RF_TXLDO_ERROR_NTF ,
. ntf = nxp_nci_rf_txldo_error_ntf ,
} ,
} ;
2021-07-24 23:47:33 +02:00
static const struct nci_ops nxp_nci_ops = {
2015-03-09 11:12:04 +01:00
. open = nxp_nci_open ,
. close = nxp_nci_close ,
. send = nxp_nci_send ,
. fw_download = nxp_nci_fw_download ,
2022-07-12 19:00:10 +02:00
. core_ops = nxp_nci_core_ops ,
. n_core_ops = ARRAY_SIZE ( nxp_nci_core_ops ) ,
2015-03-09 11:12:04 +01:00
} ;
int nxp_nci_probe ( void * phy_id , struct device * pdev ,
2015-10-11 13:24:13 +02:00
const struct nxp_nci_phy_ops * phy_ops ,
unsigned int max_payload ,
2015-03-09 11:12:04 +01:00
struct nci_dev * * ndev )
{
struct nxp_nci_info * info ;
int r ;
info = devm_kzalloc ( pdev , sizeof ( struct nxp_nci_info ) , GFP_KERNEL ) ;
2021-06-18 17:10:16 +08:00
if ( ! info )
return - ENOMEM ;
2015-03-09 11:12:04 +01:00
info - > phy_id = phy_id ;
info - > pdev = pdev ;
info - > phy_ops = phy_ops ;
info - > max_payload = max_payload ;
INIT_WORK ( & info - > fw_info . work , nxp_nci_fw_work ) ;
init_completion ( & info - > fw_info . cmd_completion ) ;
mutex_init ( & info - > info_lock ) ;
if ( info - > phy_ops - > set_mode ) {
r = info - > phy_ops - > set_mode ( info - > phy_id , NXP_NCI_MODE_COLD ) ;
if ( r < 0 )
2021-06-18 17:10:16 +08:00
return r ;
2015-03-09 11:12:04 +01:00
}
info - > mode = NXP_NCI_MODE_COLD ;
info - > ndev = nci_allocate_device ( & nxp_nci_ops , NXP_NCI_NFC_PROTOCOLS ,
NXP_NCI_HDR_LEN , 0 ) ;
2021-06-18 17:10:16 +08:00
if ( ! info - > ndev )
return - ENOMEM ;
2015-03-09 11:12:04 +01:00
nci_set_parent_dev ( info - > ndev , pdev ) ;
nci_set_drvdata ( info - > ndev , info ) ;
r = nci_register_device ( info - > ndev ) ;
2021-06-18 17:10:16 +08:00
if ( r < 0 ) {
nci_free_device ( info - > ndev ) ;
return r ;
}
2015-03-09 11:12:04 +01:00
* ndev = info - > ndev ;
return r ;
}
EXPORT_SYMBOL ( nxp_nci_probe ) ;
void nxp_nci_remove ( struct nci_dev * ndev )
{
struct nxp_nci_info * info = nci_get_drvdata ( ndev ) ;
if ( info - > mode = = NXP_NCI_MODE_FW )
nxp_nci_fw_work_complete ( info , - ESHUTDOWN ) ;
cancel_work_sync ( & info - > fw_info . work ) ;
mutex_lock ( & info - > info_lock ) ;
if ( info - > phy_ops - > set_mode )
info - > phy_ops - > set_mode ( info - > phy_id , NXP_NCI_MODE_COLD ) ;
nci_unregister_device ( ndev ) ;
nci_free_device ( ndev ) ;
mutex_unlock ( & info - > info_lock ) ;
}
EXPORT_SYMBOL ( nxp_nci_remove ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " NXP NCI NFC driver " ) ;
MODULE_AUTHOR ( " Clément Perrochaud <clement.perrochaud@nxp.com> " ) ;