18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Marvell NFC driver: major functions 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Marvell International Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by Marvell International 78c2ecf20Sopenharmony_ci * Ltd. under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available on the worldwide web at 118c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 148c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 158c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 168c2ecf20Sopenharmony_ci * this warranty disclaimer. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 238c2ecf20Sopenharmony_ci#include <linux/nfc.h> 248c2ecf20Sopenharmony_ci#include <net/nfc/nci.h> 258c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 268c2ecf20Sopenharmony_ci#include "nfcmrvl.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int nfcmrvl_nci_open(struct nci_dev *ndev) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 318c2ecf20Sopenharmony_ci int err; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* Reset possible fault of previous session */ 378c2ecf20Sopenharmony_ci clear_bit(NFCMRVL_PHY_ERROR, &priv->flags); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci err = priv->if_ops->nci_open(priv); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (err) 428c2ecf20Sopenharmony_ci clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return err; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int nfcmrvl_nci_close(struct nci_dev *ndev) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci priv->if_ops->nci_close(priv); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci nfc_info(priv->dev, "send entry, len %d\n", skb->len); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci skb->dev = (void *)ndev; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (priv->config.hci_muxed) { 688c2ecf20Sopenharmony_ci unsigned char *hdr; 698c2ecf20Sopenharmony_ci unsigned char len = skb->len; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); 728c2ecf20Sopenharmony_ci hdr[0] = NFCMRVL_HCI_COMMAND_CODE; 738c2ecf20Sopenharmony_ci hdr[1] = NFCMRVL_HCI_OGF; 748c2ecf20Sopenharmony_ci hdr[2] = NFCMRVL_HCI_OCF; 758c2ecf20Sopenharmony_ci hdr[3] = len; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return priv->if_ops->nci_send(priv, skb); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int nfcmrvl_nci_setup(struct nci_dev *ndev) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci __u8 val = 1; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int nfcmrvl_nci_fw_download(struct nci_dev *ndev, 908c2ecf20Sopenharmony_ci const char *firmware_name) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci return nfcmrvl_fw_dnld_start(ndev, firmware_name); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic struct nci_ops nfcmrvl_nci_ops = { 968c2ecf20Sopenharmony_ci .open = nfcmrvl_nci_open, 978c2ecf20Sopenharmony_ci .close = nfcmrvl_nci_close, 988c2ecf20Sopenharmony_ci .send = nfcmrvl_nci_send, 998c2ecf20Sopenharmony_ci .setup = nfcmrvl_nci_setup, 1008c2ecf20Sopenharmony_ci .fw_download = nfcmrvl_nci_fw_download, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistruct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, 1048c2ecf20Sopenharmony_ci void *drv_data, 1058c2ecf20Sopenharmony_ci struct nfcmrvl_if_ops *ops, 1068c2ecf20Sopenharmony_ci struct device *dev, 1078c2ecf20Sopenharmony_ci struct nfcmrvl_platform_data *pdata) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv; 1108c2ecf20Sopenharmony_ci int rc; 1118c2ecf20Sopenharmony_ci int headroom; 1128c2ecf20Sopenharmony_ci int tailroom; 1138c2ecf20Sopenharmony_ci u32 protocols; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1168c2ecf20Sopenharmony_ci if (!priv) 1178c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci priv->drv_data = drv_data; 1208c2ecf20Sopenharmony_ci priv->if_ops = ops; 1218c2ecf20Sopenharmony_ci priv->dev = dev; 1228c2ecf20Sopenharmony_ci priv->phy = phy; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci memcpy(&priv->config, pdata, sizeof(*pdata)); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->config.reset_n_io)) { 1278c2ecf20Sopenharmony_ci rc = gpio_request_one(priv->config.reset_n_io, 1288c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_LOW, 1298c2ecf20Sopenharmony_ci "nfcmrvl_reset_n"); 1308c2ecf20Sopenharmony_ci if (rc < 0) { 1318c2ecf20Sopenharmony_ci priv->config.reset_n_io = -EINVAL; 1328c2ecf20Sopenharmony_ci nfc_err(dev, "failed to request reset_n io\n"); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (phy == NFCMRVL_PHY_SPI) { 1378c2ecf20Sopenharmony_ci headroom = NCI_SPI_HDR_LEN; 1388c2ecf20Sopenharmony_ci tailroom = 1; 1398c2ecf20Sopenharmony_ci } else 1408c2ecf20Sopenharmony_ci headroom = tailroom = 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (priv->config.hci_muxed) 1438c2ecf20Sopenharmony_ci headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci protocols = NFC_PROTO_JEWEL_MASK 1468c2ecf20Sopenharmony_ci | NFC_PROTO_MIFARE_MASK 1478c2ecf20Sopenharmony_ci | NFC_PROTO_FELICA_MASK 1488c2ecf20Sopenharmony_ci | NFC_PROTO_ISO14443_MASK 1498c2ecf20Sopenharmony_ci | NFC_PROTO_ISO14443_B_MASK 1508c2ecf20Sopenharmony_ci | NFC_PROTO_ISO15693_MASK 1518c2ecf20Sopenharmony_ci | NFC_PROTO_NFC_DEP_MASK; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 1548c2ecf20Sopenharmony_ci headroom, tailroom); 1558c2ecf20Sopenharmony_ci if (!priv->ndev) { 1568c2ecf20Sopenharmony_ci nfc_err(dev, "nci_allocate_device failed\n"); 1578c2ecf20Sopenharmony_ci rc = -ENOMEM; 1588c2ecf20Sopenharmony_ci goto error_free_gpio; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci rc = nfcmrvl_fw_dnld_init(priv); 1628c2ecf20Sopenharmony_ci if (rc) { 1638c2ecf20Sopenharmony_ci nfc_err(dev, "failed to initialize FW download %d\n", rc); 1648c2ecf20Sopenharmony_ci goto error_free_dev; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci nci_set_drvdata(priv->ndev, priv); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rc = nci_register_device(priv->ndev); 1708c2ecf20Sopenharmony_ci if (rc) { 1718c2ecf20Sopenharmony_ci nfc_err(dev, "nci_register_device failed %d\n", rc); 1728c2ecf20Sopenharmony_ci goto error_fw_dnld_deinit; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Ensure that controller is powered off */ 1768c2ecf20Sopenharmony_ci nfcmrvl_chip_halt(priv); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci nfc_info(dev, "registered with nci successfully\n"); 1798c2ecf20Sopenharmony_ci return priv; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cierror_fw_dnld_deinit: 1828c2ecf20Sopenharmony_ci nfcmrvl_fw_dnld_deinit(priv); 1838c2ecf20Sopenharmony_cierror_free_dev: 1848c2ecf20Sopenharmony_ci nci_free_device(priv->ndev); 1858c2ecf20Sopenharmony_cierror_free_gpio: 1868c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->config.reset_n_io)) 1878c2ecf20Sopenharmony_ci gpio_free(priv->config.reset_n_io); 1888c2ecf20Sopenharmony_ci kfree(priv); 1898c2ecf20Sopenharmony_ci return ERR_PTR(rc); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_civoid nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct nci_dev *ndev = priv->ndev; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci nci_unregister_device(ndev); 1988c2ecf20Sopenharmony_ci if (priv->ndev->nfc_dev->fw_download_in_progress) 1998c2ecf20Sopenharmony_ci nfcmrvl_fw_dnld_abort(priv); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci nfcmrvl_fw_dnld_deinit(priv); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->config.reset_n_io)) 2048c2ecf20Sopenharmony_ci gpio_free(priv->config.reset_n_io); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci nci_free_device(ndev); 2078c2ecf20Sopenharmony_ci kfree(priv); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciint nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci if (priv->config.hci_muxed) { 2148c2ecf20Sopenharmony_ci if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE && 2158c2ecf20Sopenharmony_ci skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) { 2168c2ecf20Sopenharmony_ci /* Data packet, let's extract NCI payload */ 2178c2ecf20Sopenharmony_ci skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); 2188c2ecf20Sopenharmony_ci } else { 2198c2ecf20Sopenharmony_ci /* Skip this packet */ 2208c2ecf20Sopenharmony_ci kfree_skb(skb); 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (priv->ndev->nfc_dev->fw_download_in_progress) { 2268c2ecf20Sopenharmony_ci nfcmrvl_fw_dnld_recv_frame(priv, skb); 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) 2318c2ecf20Sopenharmony_ci nci_recv_frame(priv->ndev, skb); 2328c2ecf20Sopenharmony_ci else { 2338c2ecf20Sopenharmony_ci /* Drop this packet since nobody wants it */ 2348c2ecf20Sopenharmony_ci kfree_skb(skb); 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_civoid nfcmrvl_chip_reset(struct nfcmrvl_private *priv) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci /* Reset possible fault of previous session */ 2458c2ecf20Sopenharmony_ci clear_bit(NFCMRVL_PHY_ERROR, &priv->flags); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->config.reset_n_io)) { 2488c2ecf20Sopenharmony_ci nfc_info(priv->dev, "reset the chip\n"); 2498c2ecf20Sopenharmony_ci gpio_set_value(priv->config.reset_n_io, 0); 2508c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 2518c2ecf20Sopenharmony_ci gpio_set_value(priv->config.reset_n_io, 1); 2528c2ecf20Sopenharmony_ci } else 2538c2ecf20Sopenharmony_ci nfc_info(priv->dev, "no reset available on this interface\n"); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_civoid nfcmrvl_chip_halt(struct nfcmrvl_private *priv) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci if (gpio_is_valid(priv->config.reset_n_io)) 2598c2ecf20Sopenharmony_ci gpio_set_value(priv->config.reset_n_io, 0); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint nfcmrvl_parse_dt(struct device_node *node, 2638c2ecf20Sopenharmony_ci struct nfcmrvl_platform_data *pdata) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci int reset_n_io; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci reset_n_io = of_get_named_gpio(node, "reset-n-io", 0); 2688c2ecf20Sopenharmony_ci if (reset_n_io < 0) { 2698c2ecf20Sopenharmony_ci pr_info("no reset-n-io config\n"); 2708c2ecf20Sopenharmony_ci } else if (!gpio_is_valid(reset_n_io)) { 2718c2ecf20Sopenharmony_ci pr_err("invalid reset-n-io GPIO\n"); 2728c2ecf20Sopenharmony_ci return reset_n_io; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci pdata->reset_n_io = reset_n_io; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (of_find_property(node, "hci-muxed", NULL)) 2778c2ecf20Sopenharmony_ci pdata->hci_muxed = 1; 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci pdata->hci_muxed = 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfcmrvl_parse_dt); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 2868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell NFC driver"); 2878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 288