1*caf6e49bSVincent Cuissard /** 2*caf6e49bSVincent Cuissard * Marvell NFC-over-SPI driver: SPI interface related functions 3*caf6e49bSVincent Cuissard * 4*caf6e49bSVincent Cuissard * Copyright (C) 2015, Marvell International Ltd. 5*caf6e49bSVincent Cuissard * 6*caf6e49bSVincent Cuissard * This software file (the "File") is distributed by Marvell International 7*caf6e49bSVincent Cuissard * Ltd. under the terms of the GNU General Public License Version 2, June 1991 8*caf6e49bSVincent Cuissard * (the "License"). You may use, redistribute and/or modify this File in 9*caf6e49bSVincent Cuissard * accordance with the terms and conditions of the License, a copy of which 10*caf6e49bSVincent Cuissard * is available on the worldwide web at 11*caf6e49bSVincent Cuissard * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 12*caf6e49bSVincent Cuissard * 13*caf6e49bSVincent Cuissard * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 14*caf6e49bSVincent Cuissard * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 15*caf6e49bSVincent Cuissard * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 16*caf6e49bSVincent Cuissard * this warranty disclaimer. 17*caf6e49bSVincent Cuissard **/ 18*caf6e49bSVincent Cuissard 19*caf6e49bSVincent Cuissard #include <linux/module.h> 20*caf6e49bSVincent Cuissard #include <linux/interrupt.h> 21*caf6e49bSVincent Cuissard #include <linux/pm_runtime.h> 22*caf6e49bSVincent Cuissard #include <linux/nfc.h> 23*caf6e49bSVincent Cuissard #include <linux/gpio.h> 24*caf6e49bSVincent Cuissard #include <linux/of_irq.h> 25*caf6e49bSVincent Cuissard #include <linux/of_gpio.h> 26*caf6e49bSVincent Cuissard #include <net/nfc/nci.h> 27*caf6e49bSVincent Cuissard #include <net/nfc/nci_core.h> 28*caf6e49bSVincent Cuissard #include <linux/spi/spi.h> 29*caf6e49bSVincent Cuissard #include <linux/gpio.h> 30*caf6e49bSVincent Cuissard #include "nfcmrvl.h" 31*caf6e49bSVincent Cuissard 32*caf6e49bSVincent Cuissard #define SPI_WAIT_HANDSHAKE 1 33*caf6e49bSVincent Cuissard 34*caf6e49bSVincent Cuissard struct nfcmrvl_spi_drv_data { 35*caf6e49bSVincent Cuissard unsigned long flags; 36*caf6e49bSVincent Cuissard struct spi_device *spi; 37*caf6e49bSVincent Cuissard struct nci_spi *nci_spi; 38*caf6e49bSVincent Cuissard struct completion handshake_completion; 39*caf6e49bSVincent Cuissard struct nfcmrvl_private *priv; 40*caf6e49bSVincent Cuissard }; 41*caf6e49bSVincent Cuissard 42*caf6e49bSVincent Cuissard static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr) 43*caf6e49bSVincent Cuissard { 44*caf6e49bSVincent Cuissard struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr; 45*caf6e49bSVincent Cuissard struct sk_buff *skb; 46*caf6e49bSVincent Cuissard 47*caf6e49bSVincent Cuissard /* 48*caf6e49bSVincent Cuissard * Special case where we are waiting for SPI_INT deassertion to start a 49*caf6e49bSVincent Cuissard * transfer. 50*caf6e49bSVincent Cuissard */ 51*caf6e49bSVincent Cuissard if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) { 52*caf6e49bSVincent Cuissard complete(&drv_data->handshake_completion); 53*caf6e49bSVincent Cuissard return IRQ_HANDLED; 54*caf6e49bSVincent Cuissard } 55*caf6e49bSVincent Cuissard 56*caf6e49bSVincent Cuissard /* Normal case, SPI_INT deasserted by slave to trigger a master read */ 57*caf6e49bSVincent Cuissard 58*caf6e49bSVincent Cuissard skb = nci_spi_read(drv_data->nci_spi); 59*caf6e49bSVincent Cuissard if (!skb) { 60*caf6e49bSVincent Cuissard nfc_err(&drv_data->spi->dev, "failed to read spi packet"); 61*caf6e49bSVincent Cuissard return IRQ_HANDLED; 62*caf6e49bSVincent Cuissard } 63*caf6e49bSVincent Cuissard 64*caf6e49bSVincent Cuissard if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) 65*caf6e49bSVincent Cuissard nfc_err(&drv_data->spi->dev, "corrupted RX packet"); 66*caf6e49bSVincent Cuissard 67*caf6e49bSVincent Cuissard return IRQ_HANDLED; 68*caf6e49bSVincent Cuissard } 69*caf6e49bSVincent Cuissard 70*caf6e49bSVincent Cuissard static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv) 71*caf6e49bSVincent Cuissard { 72*caf6e49bSVincent Cuissard return 0; 73*caf6e49bSVincent Cuissard } 74*caf6e49bSVincent Cuissard 75*caf6e49bSVincent Cuissard static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv) 76*caf6e49bSVincent Cuissard { 77*caf6e49bSVincent Cuissard return 0; 78*caf6e49bSVincent Cuissard } 79*caf6e49bSVincent Cuissard 80*caf6e49bSVincent Cuissard static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv, 81*caf6e49bSVincent Cuissard struct sk_buff *skb) 82*caf6e49bSVincent Cuissard { 83*caf6e49bSVincent Cuissard struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data; 84*caf6e49bSVincent Cuissard int err; 85*caf6e49bSVincent Cuissard 86*caf6e49bSVincent Cuissard /* Reinit completion for slave handshake */ 87*caf6e49bSVincent Cuissard reinit_completion(&drv_data->handshake_completion); 88*caf6e49bSVincent Cuissard set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags); 89*caf6e49bSVincent Cuissard 90*caf6e49bSVincent Cuissard /* 91*caf6e49bSVincent Cuissard * Append a dummy byte at the end of SPI frame. This is due to a 92*caf6e49bSVincent Cuissard * specific DMA implementation in the controller 93*caf6e49bSVincent Cuissard */ 94*caf6e49bSVincent Cuissard skb_put(skb, 1); 95*caf6e49bSVincent Cuissard 96*caf6e49bSVincent Cuissard /* Send the SPI packet */ 97*caf6e49bSVincent Cuissard err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion, 98*caf6e49bSVincent Cuissard skb); 99*caf6e49bSVincent Cuissard if (err != 0) { 100*caf6e49bSVincent Cuissard nfc_err(priv->dev, "spi_send failed %d", err); 101*caf6e49bSVincent Cuissard kfree_skb(skb); 102*caf6e49bSVincent Cuissard } 103*caf6e49bSVincent Cuissard return err; 104*caf6e49bSVincent Cuissard } 105*caf6e49bSVincent Cuissard 106*caf6e49bSVincent Cuissard static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv, 107*caf6e49bSVincent Cuissard const void *param) 108*caf6e49bSVincent Cuissard { 109*caf6e49bSVincent Cuissard struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data; 110*caf6e49bSVincent Cuissard const struct nfcmrvl_fw_spi_config *config = param; 111*caf6e49bSVincent Cuissard 112*caf6e49bSVincent Cuissard drv_data->nci_spi->xfer_speed_hz = config->clk; 113*caf6e49bSVincent Cuissard } 114*caf6e49bSVincent Cuissard 115*caf6e49bSVincent Cuissard static struct nfcmrvl_if_ops spi_ops = { 116*caf6e49bSVincent Cuissard .nci_open = nfcmrvl_spi_nci_open, 117*caf6e49bSVincent Cuissard .nci_close = nfcmrvl_spi_nci_close, 118*caf6e49bSVincent Cuissard .nci_send = nfcmrvl_spi_nci_send, 119*caf6e49bSVincent Cuissard .nci_update_config = nfcmrvl_spi_nci_update_config, 120*caf6e49bSVincent Cuissard }; 121*caf6e49bSVincent Cuissard 122*caf6e49bSVincent Cuissard static int nfcmrvl_spi_parse_dt(struct device_node *node, 123*caf6e49bSVincent Cuissard struct nfcmrvl_platform_data *pdata) 124*caf6e49bSVincent Cuissard { 125*caf6e49bSVincent Cuissard int ret; 126*caf6e49bSVincent Cuissard 127*caf6e49bSVincent Cuissard ret = nfcmrvl_parse_dt(node, pdata); 128*caf6e49bSVincent Cuissard if (ret < 0) { 129*caf6e49bSVincent Cuissard pr_err("Failed to get generic entries\n"); 130*caf6e49bSVincent Cuissard return ret; 131*caf6e49bSVincent Cuissard } 132*caf6e49bSVincent Cuissard 133*caf6e49bSVincent Cuissard ret = irq_of_parse_and_map(node, 0); 134*caf6e49bSVincent Cuissard if (ret < 0) { 135*caf6e49bSVincent Cuissard pr_err("Unable to get irq, error: %d\n", ret); 136*caf6e49bSVincent Cuissard return ret; 137*caf6e49bSVincent Cuissard } 138*caf6e49bSVincent Cuissard pdata->irq = ret; 139*caf6e49bSVincent Cuissard 140*caf6e49bSVincent Cuissard return 0; 141*caf6e49bSVincent Cuissard } 142*caf6e49bSVincent Cuissard 143*caf6e49bSVincent Cuissard static int nfcmrvl_spi_probe(struct spi_device *spi) 144*caf6e49bSVincent Cuissard { 145*caf6e49bSVincent Cuissard struct nfcmrvl_platform_data *pdata; 146*caf6e49bSVincent Cuissard struct nfcmrvl_platform_data config; 147*caf6e49bSVincent Cuissard struct nfcmrvl_spi_drv_data *drv_data; 148*caf6e49bSVincent Cuissard int ret = 0; 149*caf6e49bSVincent Cuissard 150*caf6e49bSVincent Cuissard drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL); 151*caf6e49bSVincent Cuissard if (!drv_data) 152*caf6e49bSVincent Cuissard return -ENOMEM; 153*caf6e49bSVincent Cuissard 154*caf6e49bSVincent Cuissard drv_data->spi = spi; 155*caf6e49bSVincent Cuissard drv_data->priv = NULL; 156*caf6e49bSVincent Cuissard spi_set_drvdata(spi, drv_data); 157*caf6e49bSVincent Cuissard 158*caf6e49bSVincent Cuissard pdata = spi->dev.platform_data; 159*caf6e49bSVincent Cuissard 160*caf6e49bSVincent Cuissard if (!pdata && spi->dev.of_node) 161*caf6e49bSVincent Cuissard if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0) 162*caf6e49bSVincent Cuissard pdata = &config; 163*caf6e49bSVincent Cuissard 164*caf6e49bSVincent Cuissard if (!pdata) 165*caf6e49bSVincent Cuissard return -EINVAL; 166*caf6e49bSVincent Cuissard 167*caf6e49bSVincent Cuissard ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq, 168*caf6e49bSVincent Cuissard NULL, nfcmrvl_spi_int_irq_thread_fn, 169*caf6e49bSVincent Cuissard IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 170*caf6e49bSVincent Cuissard "nfcmrvl_spi_int", drv_data); 171*caf6e49bSVincent Cuissard if (ret < 0) { 172*caf6e49bSVincent Cuissard nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler"); 173*caf6e49bSVincent Cuissard return -ENODEV; 174*caf6e49bSVincent Cuissard } 175*caf6e49bSVincent Cuissard 176*caf6e49bSVincent Cuissard drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI, 177*caf6e49bSVincent Cuissard drv_data, &spi_ops, 178*caf6e49bSVincent Cuissard &drv_data->spi->dev, 179*caf6e49bSVincent Cuissard pdata); 180*caf6e49bSVincent Cuissard if (IS_ERR(drv_data->priv)) 181*caf6e49bSVincent Cuissard return PTR_ERR(drv_data->priv); 182*caf6e49bSVincent Cuissard 183*caf6e49bSVincent Cuissard drv_data->priv->support_fw_dnld = true; 184*caf6e49bSVincent Cuissard 185*caf6e49bSVincent Cuissard drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10, 186*caf6e49bSVincent Cuissard drv_data->priv->ndev); 187*caf6e49bSVincent Cuissard 188*caf6e49bSVincent Cuissard /* Init completion for slave handshake */ 189*caf6e49bSVincent Cuissard init_completion(&drv_data->handshake_completion); 190*caf6e49bSVincent Cuissard return 0; 191*caf6e49bSVincent Cuissard } 192*caf6e49bSVincent Cuissard 193*caf6e49bSVincent Cuissard static int nfcmrvl_spi_remove(struct spi_device *spi) 194*caf6e49bSVincent Cuissard { 195*caf6e49bSVincent Cuissard struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi); 196*caf6e49bSVincent Cuissard 197*caf6e49bSVincent Cuissard nfcmrvl_nci_unregister_dev(drv_data->priv); 198*caf6e49bSVincent Cuissard return 0; 199*caf6e49bSVincent Cuissard } 200*caf6e49bSVincent Cuissard 201*caf6e49bSVincent Cuissard static const struct of_device_id of_nfcmrvl_spi_match[] = { 202*caf6e49bSVincent Cuissard { .compatible = "mrvl,nfc-spi", }, 203*caf6e49bSVincent Cuissard {}, 204*caf6e49bSVincent Cuissard }; 205*caf6e49bSVincent Cuissard MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match); 206*caf6e49bSVincent Cuissard 207*caf6e49bSVincent Cuissard static const struct spi_device_id nfcmrvl_spi_id_table[] = { 208*caf6e49bSVincent Cuissard { "nfcmrvl_spi", 0 }, 209*caf6e49bSVincent Cuissard { } 210*caf6e49bSVincent Cuissard }; 211*caf6e49bSVincent Cuissard MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table); 212*caf6e49bSVincent Cuissard 213*caf6e49bSVincent Cuissard static struct spi_driver nfcmrvl_spi_driver = { 214*caf6e49bSVincent Cuissard .probe = nfcmrvl_spi_probe, 215*caf6e49bSVincent Cuissard .remove = nfcmrvl_spi_remove, 216*caf6e49bSVincent Cuissard .id_table = nfcmrvl_spi_id_table, 217*caf6e49bSVincent Cuissard .driver = { 218*caf6e49bSVincent Cuissard .name = "nfcmrvl_spi", 219*caf6e49bSVincent Cuissard .owner = THIS_MODULE, 220*caf6e49bSVincent Cuissard .of_match_table = of_match_ptr(of_nfcmrvl_spi_match), 221*caf6e49bSVincent Cuissard }, 222*caf6e49bSVincent Cuissard }; 223*caf6e49bSVincent Cuissard 224*caf6e49bSVincent Cuissard module_spi_driver(nfcmrvl_spi_driver); 225*caf6e49bSVincent Cuissard 226*caf6e49bSVincent Cuissard MODULE_AUTHOR("Marvell International Ltd."); 227*caf6e49bSVincent Cuissard MODULE_DESCRIPTION("Marvell NFC-over-SPI driver"); 228*caf6e49bSVincent Cuissard MODULE_LICENSE("GPL v2"); 229