1f26e30ccSAmitkumar Karwar /** 2f26e30ccSAmitkumar Karwar * Marvell NFC-over-USB driver: USB interface related functions 3f26e30ccSAmitkumar Karwar * 4f26e30ccSAmitkumar Karwar * Copyright (C) 2014, Marvell International Ltd. 5f26e30ccSAmitkumar Karwar * 6f26e30ccSAmitkumar Karwar * This software file (the "File") is distributed by Marvell International 7f26e30ccSAmitkumar Karwar * Ltd. under the terms of the GNU General Public License Version 2, June 1991 8f26e30ccSAmitkumar Karwar * (the "License"). You may use, redistribute and/or modify this File in 9f26e30ccSAmitkumar Karwar * accordance with the terms and conditions of the License, a copy of which 10f26e30ccSAmitkumar Karwar * is available on the worldwide web at 11f26e30ccSAmitkumar Karwar * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 12f26e30ccSAmitkumar Karwar * 13f26e30ccSAmitkumar Karwar * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 14f26e30ccSAmitkumar Karwar * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 15f26e30ccSAmitkumar Karwar * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 16f26e30ccSAmitkumar Karwar * this warranty disclaimer. 17f26e30ccSAmitkumar Karwar **/ 18f26e30ccSAmitkumar Karwar 19f26e30ccSAmitkumar Karwar #include <linux/module.h> 20f26e30ccSAmitkumar Karwar #include <linux/usb.h> 21f26e30ccSAmitkumar Karwar #include <linux/nfc.h> 22f26e30ccSAmitkumar Karwar #include <net/nfc/nci.h> 23f26e30ccSAmitkumar Karwar #include <net/nfc/nci_core.h> 24f26e30ccSAmitkumar Karwar #include "nfcmrvl.h" 25f26e30ccSAmitkumar Karwar 26f26e30ccSAmitkumar Karwar static struct usb_device_id nfcmrvl_table[] = { 278a81a96bSVincent Cuissard { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046, 288a81a96bSVincent Cuissard USB_CLASS_VENDOR_SPEC, 4, 1) }, 29f26e30ccSAmitkumar Karwar { } /* Terminating entry */ 30f26e30ccSAmitkumar Karwar }; 31f26e30ccSAmitkumar Karwar 32f26e30ccSAmitkumar Karwar MODULE_DEVICE_TABLE(usb, nfcmrvl_table); 33f26e30ccSAmitkumar Karwar 34f26e30ccSAmitkumar Karwar #define NFCMRVL_USB_BULK_RUNNING 1 35f26e30ccSAmitkumar Karwar #define NFCMRVL_USB_SUSPENDING 2 36f26e30ccSAmitkumar Karwar 37f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data { 38f26e30ccSAmitkumar Karwar struct usb_device *udev; 39f26e30ccSAmitkumar Karwar struct usb_interface *intf; 40f26e30ccSAmitkumar Karwar unsigned long flags; 41f26e30ccSAmitkumar Karwar struct work_struct waker; 42f26e30ccSAmitkumar Karwar struct usb_anchor tx_anchor; 43f26e30ccSAmitkumar Karwar struct usb_anchor bulk_anchor; 44f26e30ccSAmitkumar Karwar struct usb_anchor deferred; 45f26e30ccSAmitkumar Karwar int tx_in_flight; 46f26e30ccSAmitkumar Karwar /* protects tx_in_flight */ 47f26e30ccSAmitkumar Karwar spinlock_t txlock; 48f26e30ccSAmitkumar Karwar struct usb_endpoint_descriptor *bulk_tx_ep; 49f26e30ccSAmitkumar Karwar struct usb_endpoint_descriptor *bulk_rx_ep; 50f26e30ccSAmitkumar Karwar int suspend_count; 51f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv; 52f26e30ccSAmitkumar Karwar }; 53f26e30ccSAmitkumar Karwar 54f26e30ccSAmitkumar Karwar static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) 55f26e30ccSAmitkumar Karwar { 56f26e30ccSAmitkumar Karwar unsigned long flags; 57f26e30ccSAmitkumar Karwar int rv; 58f26e30ccSAmitkumar Karwar 59f26e30ccSAmitkumar Karwar spin_lock_irqsave(&drv_data->txlock, flags); 60f26e30ccSAmitkumar Karwar rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 61f26e30ccSAmitkumar Karwar if (!rv) 62f26e30ccSAmitkumar Karwar drv_data->tx_in_flight++; 63f26e30ccSAmitkumar Karwar spin_unlock_irqrestore(&drv_data->txlock, flags); 64f26e30ccSAmitkumar Karwar 65f26e30ccSAmitkumar Karwar return rv; 66f26e30ccSAmitkumar Karwar } 67f26e30ccSAmitkumar Karwar 68f26e30ccSAmitkumar Karwar static void nfcmrvl_bulk_complete(struct urb *urb) 69f26e30ccSAmitkumar Karwar { 70f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = urb->context; 71e1bf80c2SVincent Cuissard struct sk_buff *skb; 72f26e30ccSAmitkumar Karwar int err; 73f26e30ccSAmitkumar Karwar 74e1bf80c2SVincent Cuissard dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n", 75f26e30ccSAmitkumar Karwar urb, urb->status, urb->actual_length); 76f26e30ccSAmitkumar Karwar 77f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 78f26e30ccSAmitkumar Karwar return; 79f26e30ccSAmitkumar Karwar 80f26e30ccSAmitkumar Karwar if (!urb->status) { 81e1bf80c2SVincent Cuissard skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, 82e1bf80c2SVincent Cuissard GFP_ATOMIC); 83e1bf80c2SVincent Cuissard if (!skb) { 84e1bf80c2SVincent Cuissard nfc_err(&drv_data->udev->dev, "failed to alloc mem\n"); 85e1bf80c2SVincent Cuissard } else { 86e1bf80c2SVincent Cuissard memcpy(skb_put(skb, urb->actual_length), 87e1bf80c2SVincent Cuissard urb->transfer_buffer, urb->actual_length); 88e1bf80c2SVincent Cuissard if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) 89e1bf80c2SVincent Cuissard nfc_err(&drv_data->udev->dev, 90e1bf80c2SVincent Cuissard "corrupted Rx packet\n"); 91e1bf80c2SVincent Cuissard } 92f26e30ccSAmitkumar Karwar } 93f26e30ccSAmitkumar Karwar 94f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) 95f26e30ccSAmitkumar Karwar return; 96f26e30ccSAmitkumar Karwar 97f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->bulk_anchor); 98f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 99f26e30ccSAmitkumar Karwar 100f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 101f26e30ccSAmitkumar Karwar if (err) { 102f26e30ccSAmitkumar Karwar /* -EPERM: urb is being killed; 103f26e30ccSAmitkumar Karwar * -ENODEV: device got disconnected 104f26e30ccSAmitkumar Karwar */ 105f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 106f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 1073590ebc0SJoe Perches "urb %p failed to resubmit (%d)\n", urb, -err); 108f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 109f26e30ccSAmitkumar Karwar } 110f26e30ccSAmitkumar Karwar } 111f26e30ccSAmitkumar Karwar 112f26e30ccSAmitkumar Karwar static int 113f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) 114f26e30ccSAmitkumar Karwar { 115f26e30ccSAmitkumar Karwar struct urb *urb; 116f26e30ccSAmitkumar Karwar unsigned char *buf; 117f26e30ccSAmitkumar Karwar unsigned int pipe; 118f26e30ccSAmitkumar Karwar int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; 119f26e30ccSAmitkumar Karwar 120f26e30ccSAmitkumar Karwar if (!drv_data->bulk_rx_ep) 121f26e30ccSAmitkumar Karwar return -ENODEV; 122f26e30ccSAmitkumar Karwar 123f26e30ccSAmitkumar Karwar urb = usb_alloc_urb(0, mem_flags); 124f26e30ccSAmitkumar Karwar if (!urb) 125f26e30ccSAmitkumar Karwar return -ENOMEM; 126f26e30ccSAmitkumar Karwar 127f26e30ccSAmitkumar Karwar buf = kmalloc(size, mem_flags); 128f26e30ccSAmitkumar Karwar if (!buf) { 129f26e30ccSAmitkumar Karwar usb_free_urb(urb); 130f26e30ccSAmitkumar Karwar return -ENOMEM; 131f26e30ccSAmitkumar Karwar } 132f26e30ccSAmitkumar Karwar 133f26e30ccSAmitkumar Karwar pipe = usb_rcvbulkpipe(drv_data->udev, 134f26e30ccSAmitkumar Karwar drv_data->bulk_rx_ep->bEndpointAddress); 135f26e30ccSAmitkumar Karwar 136f26e30ccSAmitkumar Karwar usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, 137f26e30ccSAmitkumar Karwar nfcmrvl_bulk_complete, drv_data); 138f26e30ccSAmitkumar Karwar 139f26e30ccSAmitkumar Karwar urb->transfer_flags |= URB_FREE_BUFFER; 140f26e30ccSAmitkumar Karwar 141f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 142f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->bulk_anchor); 143f26e30ccSAmitkumar Karwar 144f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, mem_flags); 145f26e30ccSAmitkumar Karwar if (err) { 146f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 147f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 1483590ebc0SJoe Perches "urb %p submission failed (%d)\n", urb, -err); 149f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 150f26e30ccSAmitkumar Karwar } 151f26e30ccSAmitkumar Karwar 152f26e30ccSAmitkumar Karwar usb_free_urb(urb); 153f26e30ccSAmitkumar Karwar 154f26e30ccSAmitkumar Karwar return err; 155f26e30ccSAmitkumar Karwar } 156f26e30ccSAmitkumar Karwar 157f26e30ccSAmitkumar Karwar static void nfcmrvl_tx_complete(struct urb *urb) 158f26e30ccSAmitkumar Karwar { 159f26e30ccSAmitkumar Karwar struct sk_buff *skb = urb->context; 160f26e30ccSAmitkumar Karwar struct nci_dev *ndev = (struct nci_dev *)skb->dev; 161f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 162f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 163f26e30ccSAmitkumar Karwar 1643590ebc0SJoe Perches nfc_info(priv->dev, "urb %p status %d count %d\n", 165f26e30ccSAmitkumar Karwar urb, urb->status, urb->actual_length); 166f26e30ccSAmitkumar Karwar 167f26e30ccSAmitkumar Karwar spin_lock(&drv_data->txlock); 168f26e30ccSAmitkumar Karwar drv_data->tx_in_flight--; 169f26e30ccSAmitkumar Karwar spin_unlock(&drv_data->txlock); 170f26e30ccSAmitkumar Karwar 171f26e30ccSAmitkumar Karwar kfree(urb->setup_packet); 172f26e30ccSAmitkumar Karwar kfree_skb(skb); 173f26e30ccSAmitkumar Karwar } 174f26e30ccSAmitkumar Karwar 175f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) 176f26e30ccSAmitkumar Karwar { 177f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 178f26e30ccSAmitkumar Karwar int err; 179f26e30ccSAmitkumar Karwar 180f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 181f26e30ccSAmitkumar Karwar if (err) 182f26e30ccSAmitkumar Karwar return err; 183f26e30ccSAmitkumar Karwar 184f26e30ccSAmitkumar Karwar drv_data->intf->needs_remote_wakeup = 1; 185f26e30ccSAmitkumar Karwar 186f26e30ccSAmitkumar Karwar err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 187f26e30ccSAmitkumar Karwar if (err) 188f26e30ccSAmitkumar Karwar goto failed; 189f26e30ccSAmitkumar Karwar 190f26e30ccSAmitkumar Karwar set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 191f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 192f26e30ccSAmitkumar Karwar 193f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 194f26e30ccSAmitkumar Karwar return 0; 195f26e30ccSAmitkumar Karwar 196f26e30ccSAmitkumar Karwar failed: 197f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 198f26e30ccSAmitkumar Karwar return err; 199f26e30ccSAmitkumar Karwar } 200f26e30ccSAmitkumar Karwar 201f26e30ccSAmitkumar Karwar static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) 202f26e30ccSAmitkumar Karwar { 203f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->bulk_anchor); 204f26e30ccSAmitkumar Karwar } 205f26e30ccSAmitkumar Karwar 206f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) 207f26e30ccSAmitkumar Karwar { 208f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 209f26e30ccSAmitkumar Karwar int err; 210f26e30ccSAmitkumar Karwar 211f26e30ccSAmitkumar Karwar cancel_work_sync(&drv_data->waker); 212f26e30ccSAmitkumar Karwar 213f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 214f26e30ccSAmitkumar Karwar 215f26e30ccSAmitkumar Karwar nfcmrvl_usb_stop_traffic(drv_data); 216f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->tx_anchor); 217f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 218f26e30ccSAmitkumar Karwar if (err) 219f26e30ccSAmitkumar Karwar goto failed; 220f26e30ccSAmitkumar Karwar 221f26e30ccSAmitkumar Karwar drv_data->intf->needs_remote_wakeup = 0; 222f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 223f26e30ccSAmitkumar Karwar 224f26e30ccSAmitkumar Karwar failed: 225f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 226f26e30ccSAmitkumar Karwar return 0; 227f26e30ccSAmitkumar Karwar } 228f26e30ccSAmitkumar Karwar 229f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, 230f26e30ccSAmitkumar Karwar struct sk_buff *skb) 231f26e30ccSAmitkumar Karwar { 232f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 233f26e30ccSAmitkumar Karwar struct urb *urb; 234f26e30ccSAmitkumar Karwar unsigned int pipe; 235f26e30ccSAmitkumar Karwar int err; 236f26e30ccSAmitkumar Karwar 237f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep) 238f26e30ccSAmitkumar Karwar return -ENODEV; 239f26e30ccSAmitkumar Karwar 240f26e30ccSAmitkumar Karwar urb = usb_alloc_urb(0, GFP_ATOMIC); 241f26e30ccSAmitkumar Karwar if (!urb) 242f26e30ccSAmitkumar Karwar return -ENOMEM; 243f26e30ccSAmitkumar Karwar 244f26e30ccSAmitkumar Karwar pipe = usb_sndbulkpipe(drv_data->udev, 245f26e30ccSAmitkumar Karwar drv_data->bulk_tx_ep->bEndpointAddress); 246f26e30ccSAmitkumar Karwar 247f26e30ccSAmitkumar Karwar usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, 248f26e30ccSAmitkumar Karwar nfcmrvl_tx_complete, skb); 249f26e30ccSAmitkumar Karwar 250f26e30ccSAmitkumar Karwar err = nfcmrvl_inc_tx(drv_data); 251f26e30ccSAmitkumar Karwar if (err) { 252f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->deferred); 253f26e30ccSAmitkumar Karwar schedule_work(&drv_data->waker); 254f26e30ccSAmitkumar Karwar err = 0; 255f26e30ccSAmitkumar Karwar goto done; 256f26e30ccSAmitkumar Karwar } 257f26e30ccSAmitkumar Karwar 258f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->tx_anchor); 259f26e30ccSAmitkumar Karwar 260f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 261f26e30ccSAmitkumar Karwar if (err) { 262f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 263f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 2643590ebc0SJoe Perches "urb %p submission failed (%d)\n", urb, -err); 265f26e30ccSAmitkumar Karwar kfree(urb->setup_packet); 266f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 267f26e30ccSAmitkumar Karwar } else { 268f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 269f26e30ccSAmitkumar Karwar } 270f26e30ccSAmitkumar Karwar 271f26e30ccSAmitkumar Karwar done: 272f26e30ccSAmitkumar Karwar usb_free_urb(urb); 273f26e30ccSAmitkumar Karwar return err; 274f26e30ccSAmitkumar Karwar } 275f26e30ccSAmitkumar Karwar 276f26e30ccSAmitkumar Karwar static struct nfcmrvl_if_ops usb_ops = { 277f26e30ccSAmitkumar Karwar .nci_open = nfcmrvl_usb_nci_open, 278f26e30ccSAmitkumar Karwar .nci_close = nfcmrvl_usb_nci_close, 279f26e30ccSAmitkumar Karwar .nci_send = nfcmrvl_usb_nci_send, 280f26e30ccSAmitkumar Karwar }; 281f26e30ccSAmitkumar Karwar 282f26e30ccSAmitkumar Karwar static void nfcmrvl_waker(struct work_struct *work) 283f26e30ccSAmitkumar Karwar { 284f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = 285f26e30ccSAmitkumar Karwar container_of(work, struct nfcmrvl_usb_drv_data, waker); 286f26e30ccSAmitkumar Karwar int err; 287f26e30ccSAmitkumar Karwar 288f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 289f26e30ccSAmitkumar Karwar if (err) 290f26e30ccSAmitkumar Karwar return; 291f26e30ccSAmitkumar Karwar 292f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 293f26e30ccSAmitkumar Karwar } 294f26e30ccSAmitkumar Karwar 295f26e30ccSAmitkumar Karwar static int nfcmrvl_probe(struct usb_interface *intf, 296f26e30ccSAmitkumar Karwar const struct usb_device_id *id) 297f26e30ccSAmitkumar Karwar { 298f26e30ccSAmitkumar Karwar struct usb_endpoint_descriptor *ep_desc; 299f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data; 300f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv; 301f26e30ccSAmitkumar Karwar int i; 302f26e30ccSAmitkumar Karwar struct usb_device *udev = interface_to_usbdev(intf); 303dc14bdefSVincent Cuissard struct nfcmrvl_platform_data config; 304dc14bdefSVincent Cuissard 305dc14bdefSVincent Cuissard /* No configuration for USB */ 306dc14bdefSVincent Cuissard memset(&config, 0, sizeof(config)); 307f26e30ccSAmitkumar Karwar 3083590ebc0SJoe Perches nfc_info(&udev->dev, "intf %p id %p\n", intf, id); 309f26e30ccSAmitkumar Karwar 310f26e30ccSAmitkumar Karwar drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); 311f26e30ccSAmitkumar Karwar if (!drv_data) 312f26e30ccSAmitkumar Karwar return -ENOMEM; 313f26e30ccSAmitkumar Karwar 314f26e30ccSAmitkumar Karwar for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { 315f26e30ccSAmitkumar Karwar ep_desc = &intf->cur_altsetting->endpoint[i].desc; 316f26e30ccSAmitkumar Karwar 317f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep && 318f26e30ccSAmitkumar Karwar usb_endpoint_is_bulk_out(ep_desc)) { 319f26e30ccSAmitkumar Karwar drv_data->bulk_tx_ep = ep_desc; 320f26e30ccSAmitkumar Karwar continue; 321f26e30ccSAmitkumar Karwar } 322f26e30ccSAmitkumar Karwar 323f26e30ccSAmitkumar Karwar if (!drv_data->bulk_rx_ep && 324f26e30ccSAmitkumar Karwar usb_endpoint_is_bulk_in(ep_desc)) { 325f26e30ccSAmitkumar Karwar drv_data->bulk_rx_ep = ep_desc; 326f26e30ccSAmitkumar Karwar continue; 327f26e30ccSAmitkumar Karwar } 328f26e30ccSAmitkumar Karwar } 329f26e30ccSAmitkumar Karwar 330f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) 331f26e30ccSAmitkumar Karwar return -ENODEV; 332f26e30ccSAmitkumar Karwar 333f26e30ccSAmitkumar Karwar drv_data->udev = udev; 334f26e30ccSAmitkumar Karwar drv_data->intf = intf; 335f26e30ccSAmitkumar Karwar 336f26e30ccSAmitkumar Karwar INIT_WORK(&drv_data->waker, nfcmrvl_waker); 337f26e30ccSAmitkumar Karwar spin_lock_init(&drv_data->txlock); 338f26e30ccSAmitkumar Karwar 339f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->tx_anchor); 340f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->bulk_anchor); 341f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->deferred); 342f26e30ccSAmitkumar Karwar 343f26e30ccSAmitkumar Karwar priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, 344dc14bdefSVincent Cuissard &drv_data->udev->dev, &config); 345f26e30ccSAmitkumar Karwar if (IS_ERR(priv)) 346f26e30ccSAmitkumar Karwar return PTR_ERR(priv); 347f26e30ccSAmitkumar Karwar 348f26e30ccSAmitkumar Karwar drv_data->priv = priv; 34943465b7aSVincent Cuissard drv_data->priv->phy = NFCMRVL_PHY_USB; 3503194c687SVincent Cuissard drv_data->priv->support_fw_dnld = false; 3513194c687SVincent Cuissard 352f26e30ccSAmitkumar Karwar priv->dev = &drv_data->udev->dev; 353f26e30ccSAmitkumar Karwar 354f26e30ccSAmitkumar Karwar usb_set_intfdata(intf, drv_data); 355f26e30ccSAmitkumar Karwar 356f26e30ccSAmitkumar Karwar return 0; 357f26e30ccSAmitkumar Karwar } 358f26e30ccSAmitkumar Karwar 359f26e30ccSAmitkumar Karwar static void nfcmrvl_disconnect(struct usb_interface *intf) 360f26e30ccSAmitkumar Karwar { 361f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 362f26e30ccSAmitkumar Karwar 363f26e30ccSAmitkumar Karwar if (!drv_data) 364f26e30ccSAmitkumar Karwar return; 365f26e30ccSAmitkumar Karwar 3663590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 367f26e30ccSAmitkumar Karwar 368f26e30ccSAmitkumar Karwar nfcmrvl_nci_unregister_dev(drv_data->priv); 369f26e30ccSAmitkumar Karwar 370f26e30ccSAmitkumar Karwar usb_set_intfdata(drv_data->intf, NULL); 371f26e30ccSAmitkumar Karwar } 372f26e30ccSAmitkumar Karwar 373f26e30ccSAmitkumar Karwar #ifdef CONFIG_PM 374f26e30ccSAmitkumar Karwar static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) 375f26e30ccSAmitkumar Karwar { 376f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 377f26e30ccSAmitkumar Karwar 3783590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 379f26e30ccSAmitkumar Karwar 380f26e30ccSAmitkumar Karwar if (drv_data->suspend_count++) 381f26e30ccSAmitkumar Karwar return 0; 382f26e30ccSAmitkumar Karwar 383f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 384f26e30ccSAmitkumar Karwar if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { 385f26e30ccSAmitkumar Karwar set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 386f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 387f26e30ccSAmitkumar Karwar } else { 388f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 389f26e30ccSAmitkumar Karwar drv_data->suspend_count--; 390f26e30ccSAmitkumar Karwar return -EBUSY; 391f26e30ccSAmitkumar Karwar } 392f26e30ccSAmitkumar Karwar 393f26e30ccSAmitkumar Karwar nfcmrvl_usb_stop_traffic(drv_data); 394f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->tx_anchor); 395f26e30ccSAmitkumar Karwar 396f26e30ccSAmitkumar Karwar return 0; 397f26e30ccSAmitkumar Karwar } 398f26e30ccSAmitkumar Karwar 399f26e30ccSAmitkumar Karwar static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) 400f26e30ccSAmitkumar Karwar { 401f26e30ccSAmitkumar Karwar struct urb *urb; 402f26e30ccSAmitkumar Karwar int err; 403f26e30ccSAmitkumar Karwar 404f26e30ccSAmitkumar Karwar while ((urb = usb_get_from_anchor(&drv_data->deferred))) { 405f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 406f26e30ccSAmitkumar Karwar if (err) 407f26e30ccSAmitkumar Karwar break; 408f26e30ccSAmitkumar Karwar 409f26e30ccSAmitkumar Karwar drv_data->tx_in_flight++; 410f26e30ccSAmitkumar Karwar } 411f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 412f26e30ccSAmitkumar Karwar } 413f26e30ccSAmitkumar Karwar 414f26e30ccSAmitkumar Karwar static int nfcmrvl_resume(struct usb_interface *intf) 415f26e30ccSAmitkumar Karwar { 416f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 417f26e30ccSAmitkumar Karwar int err = 0; 418f26e30ccSAmitkumar Karwar 4193590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 420f26e30ccSAmitkumar Karwar 421f26e30ccSAmitkumar Karwar if (--drv_data->suspend_count) 422f26e30ccSAmitkumar Karwar return 0; 423f26e30ccSAmitkumar Karwar 424f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 425f26e30ccSAmitkumar Karwar goto done; 426f26e30ccSAmitkumar Karwar 427f26e30ccSAmitkumar Karwar if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { 428f26e30ccSAmitkumar Karwar err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 429f26e30ccSAmitkumar Karwar if (err) { 430f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 431f26e30ccSAmitkumar Karwar goto failed; 432f26e30ccSAmitkumar Karwar } 433f26e30ccSAmitkumar Karwar 434f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 435f26e30ccSAmitkumar Karwar } 436f26e30ccSAmitkumar Karwar 437f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 438f26e30ccSAmitkumar Karwar nfcmrvl_play_deferred(drv_data); 439f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 440f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 441f26e30ccSAmitkumar Karwar 442f26e30ccSAmitkumar Karwar return 0; 443f26e30ccSAmitkumar Karwar 444f26e30ccSAmitkumar Karwar failed: 445f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 446f26e30ccSAmitkumar Karwar done: 447f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 448f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 449f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 450f26e30ccSAmitkumar Karwar 451f26e30ccSAmitkumar Karwar return err; 452f26e30ccSAmitkumar Karwar } 453f26e30ccSAmitkumar Karwar #endif 454f26e30ccSAmitkumar Karwar 455f26e30ccSAmitkumar Karwar static struct usb_driver nfcmrvl_usb_driver = { 456f26e30ccSAmitkumar Karwar .name = "nfcmrvl", 457f26e30ccSAmitkumar Karwar .probe = nfcmrvl_probe, 458f26e30ccSAmitkumar Karwar .disconnect = nfcmrvl_disconnect, 459f26e30ccSAmitkumar Karwar #ifdef CONFIG_PM 460f26e30ccSAmitkumar Karwar .suspend = nfcmrvl_suspend, 461f26e30ccSAmitkumar Karwar .resume = nfcmrvl_resume, 462f26e30ccSAmitkumar Karwar .reset_resume = nfcmrvl_resume, 463f26e30ccSAmitkumar Karwar #endif 464f26e30ccSAmitkumar Karwar .id_table = nfcmrvl_table, 465f26e30ccSAmitkumar Karwar .supports_autosuspend = 1, 466f26e30ccSAmitkumar Karwar .disable_hub_initiated_lpm = 1, 467f26e30ccSAmitkumar Karwar .soft_unbind = 1, 468f26e30ccSAmitkumar Karwar }; 469f26e30ccSAmitkumar Karwar module_usb_driver(nfcmrvl_usb_driver); 470f26e30ccSAmitkumar Karwar 471f26e30ccSAmitkumar Karwar MODULE_AUTHOR("Marvell International Ltd."); 472fb101c0eSVincent Cuissard MODULE_DESCRIPTION("Marvell NFC-over-USB driver"); 473f26e30ccSAmitkumar Karwar MODULE_LICENSE("GPL v2"); 474