2014-01-07 00:58:19 +04:00
/*
* Marvell NFC driver : major functions
*
* Copyright ( C ) 2014 , Marvell International Ltd .
*
* This software file ( the " File " ) is distributed by Marvell International
* Ltd . under the terms of the GNU General Public License Version 2 , June 1991
* ( the " License " ) . You may use , redistribute and / or modify this File in
* accordance with the terms and conditions of the License , a copy of which
* is available on the worldwide web at
* http : //www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS - IS , WITHOUT WARRANTY OF ANY KIND , AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED . The License provides additional details about
* this warranty disclaimer .
*/
# include <linux/module.h>
2015-06-11 12:25:46 +03:00
# include <linux/gpio.h>
# include <linux/delay.h>
2014-01-07 00:58:19 +04:00
# include <linux/nfc.h>
# include <net/nfc/nci.h>
# include <net/nfc/nci_core.h>
# include "nfcmrvl.h"
# define VERSION "1.0"
static int nfcmrvl_nci_open ( struct nci_dev * ndev )
{
struct nfcmrvl_private * priv = nci_get_drvdata ( ndev ) ;
int err ;
if ( test_and_set_bit ( NFCMRVL_NCI_RUNNING , & priv - > flags ) )
return 0 ;
err = priv - > if_ops - > nci_open ( priv ) ;
if ( err )
clear_bit ( NFCMRVL_NCI_RUNNING , & priv - > flags ) ;
return err ;
}
static int nfcmrvl_nci_close ( struct nci_dev * ndev )
{
struct nfcmrvl_private * priv = nci_get_drvdata ( ndev ) ;
if ( ! test_and_clear_bit ( NFCMRVL_NCI_RUNNING , & priv - > flags ) )
return 0 ;
priv - > if_ops - > nci_close ( priv ) ;
return 0 ;
}
static int nfcmrvl_nci_send ( struct nci_dev * ndev , struct sk_buff * skb )
{
struct nfcmrvl_private * priv = nci_get_drvdata ( ndev ) ;
nfc_info ( priv - > dev , " send entry, len %d \n " , skb - > len ) ;
skb - > dev = ( void * ) ndev ;
if ( ! test_bit ( NFCMRVL_NCI_RUNNING , & priv - > flags ) )
return - EBUSY ;
2015-06-11 12:25:43 +03:00
if ( priv - > hci_muxed ) {
unsigned char * hdr ;
unsigned char len = skb - > len ;
hdr = ( char * ) skb_push ( skb , NFCMRVL_HCI_EVENT_HEADER_SIZE ) ;
hdr [ 0 ] = NFCMRVL_HCI_COMMAND_CODE ;
hdr [ 1 ] = NFCMRVL_HCI_OGF ;
hdr [ 2 ] = NFCMRVL_HCI_OCF ;
hdr [ 3 ] = len ;
}
2014-01-07 00:58:19 +04:00
return priv - > if_ops - > nci_send ( priv , skb ) ;
}
2014-01-07 00:58:20 +04:00
static int nfcmrvl_nci_setup ( struct nci_dev * ndev )
{
return 0 ;
}
2014-01-07 00:58:19 +04:00
static struct nci_ops nfcmrvl_nci_ops = {
. open = nfcmrvl_nci_open ,
. close = nfcmrvl_nci_close ,
. send = nfcmrvl_nci_send ,
2014-01-07 00:58:20 +04:00
. setup = nfcmrvl_nci_setup ,
2014-01-07 00:58:19 +04:00
} ;
struct nfcmrvl_private * nfcmrvl_nci_register_dev ( void * drv_data ,
struct nfcmrvl_if_ops * ops ,
2015-06-11 12:25:43 +03:00
struct device * dev ,
unsigned int flags )
2014-01-07 00:58:19 +04:00
{
struct nfcmrvl_private * priv ;
int rc ;
2015-06-11 12:25:43 +03:00
int headroom = 0 ;
2014-01-07 00:58:19 +04:00
u32 protocols ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return ERR_PTR ( - ENOMEM ) ;
priv - > drv_data = drv_data ;
priv - > if_ops = ops ;
priv - > dev = dev ;
2015-06-11 12:25:43 +03:00
priv - > hci_muxed = ( flags & NFCMRVL_DEV_FLAG_HCI_MUXED ) ? 1 : 0 ;
2015-06-11 12:25:46 +03:00
priv - > reset_n_io = NFCMRVL_DEV_FLAG_GET_RESET_N_IO ( flags ) ;
if ( priv - > reset_n_io ) {
rc = devm_gpio_request_one ( dev ,
priv - > reset_n_io ,
GPIOF_OUT_INIT_LOW ,
" nfcmrvl_reset_n " ) ;
if ( rc < 0 )
nfc_err ( dev , " failed to request reset_n io \n " ) ;
}
2015-06-11 12:25:43 +03:00
if ( priv - > hci_muxed )
headroom = NFCMRVL_HCI_EVENT_HEADER_SIZE ;
2014-01-07 00:58:19 +04:00
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 ;
2015-06-11 12:25:43 +03:00
priv - > ndev = nci_allocate_device ( & nfcmrvl_nci_ops , protocols ,
headroom , 0 ) ;
2014-01-07 00:58:19 +04:00
if ( ! priv - > ndev ) {
2015-04-07 10:17:00 +03:00
nfc_err ( dev , " nci_allocate_device failed \n " ) ;
2014-01-08 22:52:27 +04:00
rc = - ENOMEM ;
goto error ;
2014-01-07 00:58:19 +04:00
}
nci_set_drvdata ( priv - > ndev , priv ) ;
2015-06-11 12:25:46 +03:00
nfcmrvl_chip_reset ( priv ) ;
2014-01-07 00:58:19 +04:00
rc = nci_register_device ( priv - > ndev ) ;
if ( rc ) {
2015-04-07 10:17:00 +03:00
nfc_err ( dev , " nci_register_device failed %d \n " , rc ) ;
2014-01-07 00:58:19 +04:00
nci_free_device ( priv - > ndev ) ;
2014-01-08 22:52:27 +04:00
goto error ;
2014-01-07 00:58:19 +04:00
}
nfc_info ( dev , " registered with nci successfully \n " ) ;
return priv ;
2014-01-08 22:52:27 +04:00
error :
kfree ( priv ) ;
return ERR_PTR ( rc ) ;
2014-01-07 00:58:19 +04:00
}
EXPORT_SYMBOL_GPL ( nfcmrvl_nci_register_dev ) ;
void nfcmrvl_nci_unregister_dev ( struct nfcmrvl_private * priv )
{
struct nci_dev * ndev = priv - > ndev ;
nci_unregister_device ( ndev ) ;
nci_free_device ( ndev ) ;
kfree ( priv ) ;
}
EXPORT_SYMBOL_GPL ( nfcmrvl_nci_unregister_dev ) ;
2015-06-11 12:25:44 +03:00
int nfcmrvl_nci_recv_frame ( struct nfcmrvl_private * priv , struct sk_buff * skb )
2014-01-07 00:58:19 +04:00
{
2015-06-11 12:25:43 +03:00
if ( priv - > hci_muxed ) {
if ( skb - > data [ 0 ] = = NFCMRVL_HCI_EVENT_CODE & &
skb - > data [ 1 ] = = NFCMRVL_HCI_NFC_EVENT_CODE ) {
/* Data packet, let's extract NCI payload */
skb_pull ( skb , NFCMRVL_HCI_EVENT_HEADER_SIZE ) ;
} else {
/* Skip this packet */
kfree_skb ( skb ) ;
return 0 ;
}
}
2015-06-11 12:25:44 +03:00
if ( test_bit ( NFCMRVL_NCI_RUNNING , & priv - > flags ) )
nci_recv_frame ( priv - > ndev , skb ) ;
else {
/* Drop this packet since nobody wants it */
kfree_skb ( skb ) ;
return 0 ;
}
2014-01-07 00:58:19 +04:00
2015-06-11 12:25:44 +03:00
return 0 ;
2014-01-07 00:58:19 +04:00
}
EXPORT_SYMBOL_GPL ( nfcmrvl_nci_recv_frame ) ;
2015-06-11 12:25:46 +03:00
void nfcmrvl_chip_reset ( struct nfcmrvl_private * priv )
{
/*
* This function does not take care if someone is using the device .
* To be improved .
*/
if ( priv - > reset_n_io ) {
nfc_info ( priv - > dev , " reset the chip \n " ) ;
gpio_set_value ( priv - > reset_n_io , 0 ) ;
usleep_range ( 5000 , 10000 ) ;
gpio_set_value ( priv - > reset_n_io , 1 ) ;
} else
nfc_info ( priv - > dev , " no reset available on this interface \n " ) ;
}
2014-01-07 00:58:19 +04:00
MODULE_AUTHOR ( " Marvell International Ltd. " ) ;
MODULE_DESCRIPTION ( " Marvell NFC driver ver " VERSION ) ;
MODULE_VERSION ( VERSION ) ;
MODULE_LICENSE ( " GPL v2 " ) ;