1be3d162aSKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0-only 24dd649d1SAditya Srivastava /* 3f26e30ccSAmitkumar Karwar * Marvell NFC-over-USB driver: USB interface related functions 4f26e30ccSAmitkumar Karwar * 5f26e30ccSAmitkumar Karwar * Copyright (C) 2014, Marvell International Ltd. 6be3d162aSKrzysztof Kozlowski */ 7f26e30ccSAmitkumar Karwar 8f26e30ccSAmitkumar Karwar #include <linux/module.h> 9f26e30ccSAmitkumar Karwar #include <linux/usb.h> 10f26e30ccSAmitkumar Karwar #include <linux/nfc.h> 11f26e30ccSAmitkumar Karwar #include <net/nfc/nci.h> 12f26e30ccSAmitkumar Karwar #include <net/nfc/nci_core.h> 13f26e30ccSAmitkumar Karwar #include "nfcmrvl.h" 14f26e30ccSAmitkumar Karwar 15f26e30ccSAmitkumar Karwar static struct usb_device_id nfcmrvl_table[] = { 168a81a96bSVincent Cuissard { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046, 178a81a96bSVincent Cuissard USB_CLASS_VENDOR_SPEC, 4, 1) }, 18f26e30ccSAmitkumar Karwar { } /* Terminating entry */ 19f26e30ccSAmitkumar Karwar }; 20f26e30ccSAmitkumar Karwar 21f26e30ccSAmitkumar Karwar MODULE_DEVICE_TABLE(usb, nfcmrvl_table); 22f26e30ccSAmitkumar Karwar 23f26e30ccSAmitkumar Karwar #define NFCMRVL_USB_BULK_RUNNING 1 24f26e30ccSAmitkumar Karwar #define NFCMRVL_USB_SUSPENDING 2 25f26e30ccSAmitkumar Karwar 26f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data { 27f26e30ccSAmitkumar Karwar struct usb_device *udev; 28f26e30ccSAmitkumar Karwar struct usb_interface *intf; 29f26e30ccSAmitkumar Karwar unsigned long flags; 30f26e30ccSAmitkumar Karwar struct work_struct waker; 31f26e30ccSAmitkumar Karwar struct usb_anchor tx_anchor; 32f26e30ccSAmitkumar Karwar struct usb_anchor bulk_anchor; 33f26e30ccSAmitkumar Karwar struct usb_anchor deferred; 34f26e30ccSAmitkumar Karwar int tx_in_flight; 35f26e30ccSAmitkumar Karwar /* protects tx_in_flight */ 36f26e30ccSAmitkumar Karwar spinlock_t txlock; 37f26e30ccSAmitkumar Karwar struct usb_endpoint_descriptor *bulk_tx_ep; 38f26e30ccSAmitkumar Karwar struct usb_endpoint_descriptor *bulk_rx_ep; 39f26e30ccSAmitkumar Karwar int suspend_count; 40f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv; 41f26e30ccSAmitkumar Karwar }; 42f26e30ccSAmitkumar Karwar 43f26e30ccSAmitkumar Karwar static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) 44f26e30ccSAmitkumar Karwar { 45f26e30ccSAmitkumar Karwar unsigned long flags; 46f26e30ccSAmitkumar Karwar int rv; 47f26e30ccSAmitkumar Karwar 48f26e30ccSAmitkumar Karwar spin_lock_irqsave(&drv_data->txlock, flags); 49f26e30ccSAmitkumar Karwar rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 50f26e30ccSAmitkumar Karwar if (!rv) 51f26e30ccSAmitkumar Karwar drv_data->tx_in_flight++; 52f26e30ccSAmitkumar Karwar spin_unlock_irqrestore(&drv_data->txlock, flags); 53f26e30ccSAmitkumar Karwar 54f26e30ccSAmitkumar Karwar return rv; 55f26e30ccSAmitkumar Karwar } 56f26e30ccSAmitkumar Karwar 57f26e30ccSAmitkumar Karwar static void nfcmrvl_bulk_complete(struct urb *urb) 58f26e30ccSAmitkumar Karwar { 59f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = urb->context; 60f26e30ccSAmitkumar Karwar int err; 61f26e30ccSAmitkumar Karwar 62e1bf80c2SVincent Cuissard dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n", 63f26e30ccSAmitkumar Karwar urb, urb->status, urb->actual_length); 64f26e30ccSAmitkumar Karwar 65f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 66f26e30ccSAmitkumar Karwar return; 67f26e30ccSAmitkumar Karwar 68f26e30ccSAmitkumar Karwar if (!urb->status) { 692c95e6c7SKrzysztof Kozlowski struct sk_buff *skb; 702c95e6c7SKrzysztof Kozlowski 71e1bf80c2SVincent Cuissard skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, 72e1bf80c2SVincent Cuissard GFP_ATOMIC); 73e1bf80c2SVincent Cuissard if (!skb) { 74e1bf80c2SVincent Cuissard nfc_err(&drv_data->udev->dev, "failed to alloc mem\n"); 75e1bf80c2SVincent Cuissard } else { 7659ae1d12SJohannes Berg skb_put_data(skb, urb->transfer_buffer, 7759ae1d12SJohannes Berg urb->actual_length); 78e1bf80c2SVincent Cuissard if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) 79e1bf80c2SVincent Cuissard nfc_err(&drv_data->udev->dev, 80e1bf80c2SVincent Cuissard "corrupted Rx packet\n"); 81e1bf80c2SVincent Cuissard } 82f26e30ccSAmitkumar Karwar } 83f26e30ccSAmitkumar Karwar 84f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) 85f26e30ccSAmitkumar Karwar return; 86f26e30ccSAmitkumar Karwar 87f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->bulk_anchor); 88f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 89f26e30ccSAmitkumar Karwar 90f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 91f26e30ccSAmitkumar Karwar if (err) { 92f26e30ccSAmitkumar Karwar /* -EPERM: urb is being killed; 93f26e30ccSAmitkumar Karwar * -ENODEV: device got disconnected 94f26e30ccSAmitkumar Karwar */ 95f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 96f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 973590ebc0SJoe Perches "urb %p failed to resubmit (%d)\n", urb, -err); 98f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 99f26e30ccSAmitkumar Karwar } 100f26e30ccSAmitkumar Karwar } 101f26e30ccSAmitkumar Karwar 102f26e30ccSAmitkumar Karwar static int 103f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) 104f26e30ccSAmitkumar Karwar { 105f26e30ccSAmitkumar Karwar struct urb *urb; 106f26e30ccSAmitkumar Karwar unsigned char *buf; 107f26e30ccSAmitkumar Karwar unsigned int pipe; 108f26e30ccSAmitkumar Karwar int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; 109f26e30ccSAmitkumar Karwar 110f26e30ccSAmitkumar Karwar if (!drv_data->bulk_rx_ep) 111f26e30ccSAmitkumar Karwar return -ENODEV; 112f26e30ccSAmitkumar Karwar 113f26e30ccSAmitkumar Karwar urb = usb_alloc_urb(0, mem_flags); 114f26e30ccSAmitkumar Karwar if (!urb) 115f26e30ccSAmitkumar Karwar return -ENOMEM; 116f26e30ccSAmitkumar Karwar 117f26e30ccSAmitkumar Karwar buf = kmalloc(size, mem_flags); 118f26e30ccSAmitkumar Karwar if (!buf) { 119f26e30ccSAmitkumar Karwar usb_free_urb(urb); 120f26e30ccSAmitkumar Karwar return -ENOMEM; 121f26e30ccSAmitkumar Karwar } 122f26e30ccSAmitkumar Karwar 123f26e30ccSAmitkumar Karwar pipe = usb_rcvbulkpipe(drv_data->udev, 124f26e30ccSAmitkumar Karwar drv_data->bulk_rx_ep->bEndpointAddress); 125f26e30ccSAmitkumar Karwar 126f26e30ccSAmitkumar Karwar usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, 127f26e30ccSAmitkumar Karwar nfcmrvl_bulk_complete, drv_data); 128f26e30ccSAmitkumar Karwar 129f26e30ccSAmitkumar Karwar urb->transfer_flags |= URB_FREE_BUFFER; 130f26e30ccSAmitkumar Karwar 131f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 132f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->bulk_anchor); 133f26e30ccSAmitkumar Karwar 134f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, mem_flags); 135f26e30ccSAmitkumar Karwar if (err) { 136f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 137f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 1383590ebc0SJoe Perches "urb %p submission failed (%d)\n", urb, -err); 139f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 140f26e30ccSAmitkumar Karwar } 141f26e30ccSAmitkumar Karwar 142f26e30ccSAmitkumar Karwar usb_free_urb(urb); 143f26e30ccSAmitkumar Karwar 144f26e30ccSAmitkumar Karwar return err; 145f26e30ccSAmitkumar Karwar } 146f26e30ccSAmitkumar Karwar 147f26e30ccSAmitkumar Karwar static void nfcmrvl_tx_complete(struct urb *urb) 148f26e30ccSAmitkumar Karwar { 149f26e30ccSAmitkumar Karwar struct sk_buff *skb = urb->context; 150f26e30ccSAmitkumar Karwar struct nci_dev *ndev = (struct nci_dev *)skb->dev; 151f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 152f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 15324b2068eSSebastian Andrzej Siewior unsigned long flags; 154f26e30ccSAmitkumar Karwar 1553590ebc0SJoe Perches nfc_info(priv->dev, "urb %p status %d count %d\n", 156f26e30ccSAmitkumar Karwar urb, urb->status, urb->actual_length); 157f26e30ccSAmitkumar Karwar 15824b2068eSSebastian Andrzej Siewior spin_lock_irqsave(&drv_data->txlock, flags); 159f26e30ccSAmitkumar Karwar drv_data->tx_in_flight--; 16024b2068eSSebastian Andrzej Siewior spin_unlock_irqrestore(&drv_data->txlock, flags); 161f26e30ccSAmitkumar Karwar 162f26e30ccSAmitkumar Karwar kfree(urb->setup_packet); 163f26e30ccSAmitkumar Karwar kfree_skb(skb); 164f26e30ccSAmitkumar Karwar } 165f26e30ccSAmitkumar Karwar 166f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) 167f26e30ccSAmitkumar Karwar { 168f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 169f26e30ccSAmitkumar Karwar int err; 170f26e30ccSAmitkumar Karwar 171f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 172f26e30ccSAmitkumar Karwar if (err) 173f26e30ccSAmitkumar Karwar return err; 174f26e30ccSAmitkumar Karwar 175f26e30ccSAmitkumar Karwar drv_data->intf->needs_remote_wakeup = 1; 176f26e30ccSAmitkumar Karwar 177f26e30ccSAmitkumar Karwar err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 178f26e30ccSAmitkumar Karwar if (err) 179f26e30ccSAmitkumar Karwar goto failed; 180f26e30ccSAmitkumar Karwar 181f26e30ccSAmitkumar Karwar set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 182f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 183f26e30ccSAmitkumar Karwar 184f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 185f26e30ccSAmitkumar Karwar return 0; 186f26e30ccSAmitkumar Karwar 187f26e30ccSAmitkumar Karwar failed: 188f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 189f26e30ccSAmitkumar Karwar return err; 190f26e30ccSAmitkumar Karwar } 191f26e30ccSAmitkumar Karwar 192f26e30ccSAmitkumar Karwar static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) 193f26e30ccSAmitkumar Karwar { 194f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->bulk_anchor); 195f26e30ccSAmitkumar Karwar } 196f26e30ccSAmitkumar Karwar 197f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) 198f26e30ccSAmitkumar Karwar { 199f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 200f26e30ccSAmitkumar Karwar int err; 201f26e30ccSAmitkumar Karwar 202f26e30ccSAmitkumar Karwar cancel_work_sync(&drv_data->waker); 203f26e30ccSAmitkumar Karwar 204f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 205f26e30ccSAmitkumar Karwar 206f26e30ccSAmitkumar Karwar nfcmrvl_usb_stop_traffic(drv_data); 207f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->tx_anchor); 208f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 209f26e30ccSAmitkumar Karwar if (err) 210f26e30ccSAmitkumar Karwar goto failed; 211f26e30ccSAmitkumar Karwar 212f26e30ccSAmitkumar Karwar drv_data->intf->needs_remote_wakeup = 0; 213f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 214f26e30ccSAmitkumar Karwar 215f26e30ccSAmitkumar Karwar failed: 216f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 217f26e30ccSAmitkumar Karwar return 0; 218f26e30ccSAmitkumar Karwar } 219f26e30ccSAmitkumar Karwar 220f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, 221f26e30ccSAmitkumar Karwar struct sk_buff *skb) 222f26e30ccSAmitkumar Karwar { 223f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 224f26e30ccSAmitkumar Karwar struct urb *urb; 225f26e30ccSAmitkumar Karwar unsigned int pipe; 226f26e30ccSAmitkumar Karwar int err; 227f26e30ccSAmitkumar Karwar 228f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep) 229f26e30ccSAmitkumar Karwar return -ENODEV; 230f26e30ccSAmitkumar Karwar 231f26e30ccSAmitkumar Karwar urb = usb_alloc_urb(0, GFP_ATOMIC); 232f26e30ccSAmitkumar Karwar if (!urb) 233f26e30ccSAmitkumar Karwar return -ENOMEM; 234f26e30ccSAmitkumar Karwar 235f26e30ccSAmitkumar Karwar pipe = usb_sndbulkpipe(drv_data->udev, 236f26e30ccSAmitkumar Karwar drv_data->bulk_tx_ep->bEndpointAddress); 237f26e30ccSAmitkumar Karwar 238f26e30ccSAmitkumar Karwar usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, 239f26e30ccSAmitkumar Karwar nfcmrvl_tx_complete, skb); 240f26e30ccSAmitkumar Karwar 241f26e30ccSAmitkumar Karwar err = nfcmrvl_inc_tx(drv_data); 242f26e30ccSAmitkumar Karwar if (err) { 243f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->deferred); 244f26e30ccSAmitkumar Karwar schedule_work(&drv_data->waker); 245f26e30ccSAmitkumar Karwar err = 0; 246f26e30ccSAmitkumar Karwar goto done; 247f26e30ccSAmitkumar Karwar } 248f26e30ccSAmitkumar Karwar 249f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->tx_anchor); 250f26e30ccSAmitkumar Karwar 251f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 252f26e30ccSAmitkumar Karwar if (err) { 253f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 254f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 2553590ebc0SJoe Perches "urb %p submission failed (%d)\n", urb, -err); 256f26e30ccSAmitkumar Karwar kfree(urb->setup_packet); 257f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 258f26e30ccSAmitkumar Karwar } else { 259f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 260f26e30ccSAmitkumar Karwar } 261f26e30ccSAmitkumar Karwar 262f26e30ccSAmitkumar Karwar done: 263f26e30ccSAmitkumar Karwar usb_free_urb(urb); 264f26e30ccSAmitkumar Karwar return err; 265f26e30ccSAmitkumar Karwar } 266f26e30ccSAmitkumar Karwar 267*26955037SKrzysztof Kozlowski static const struct nfcmrvl_if_ops usb_ops = { 268f26e30ccSAmitkumar Karwar .nci_open = nfcmrvl_usb_nci_open, 269f26e30ccSAmitkumar Karwar .nci_close = nfcmrvl_usb_nci_close, 270f26e30ccSAmitkumar Karwar .nci_send = nfcmrvl_usb_nci_send, 271f26e30ccSAmitkumar Karwar }; 272f26e30ccSAmitkumar Karwar 273f26e30ccSAmitkumar Karwar static void nfcmrvl_waker(struct work_struct *work) 274f26e30ccSAmitkumar Karwar { 275f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = 276f26e30ccSAmitkumar Karwar container_of(work, struct nfcmrvl_usb_drv_data, waker); 277f26e30ccSAmitkumar Karwar int err; 278f26e30ccSAmitkumar Karwar 279f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 280f26e30ccSAmitkumar Karwar if (err) 281f26e30ccSAmitkumar Karwar return; 282f26e30ccSAmitkumar Karwar 283f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 284f26e30ccSAmitkumar Karwar } 285f26e30ccSAmitkumar Karwar 286f26e30ccSAmitkumar Karwar static int nfcmrvl_probe(struct usb_interface *intf, 287f26e30ccSAmitkumar Karwar const struct usb_device_id *id) 288f26e30ccSAmitkumar Karwar { 289f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data; 290f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv; 291f26e30ccSAmitkumar Karwar int i; 292f26e30ccSAmitkumar Karwar struct usb_device *udev = interface_to_usbdev(intf); 293dc14bdefSVincent Cuissard struct nfcmrvl_platform_data config; 294dc14bdefSVincent Cuissard 295dc14bdefSVincent Cuissard /* No configuration for USB */ 296dc14bdefSVincent Cuissard memset(&config, 0, sizeof(config)); 297c3953a3cSJohan Hovold config.reset_n_io = -EINVAL; 298f26e30ccSAmitkumar Karwar 2993590ebc0SJoe Perches nfc_info(&udev->dev, "intf %p id %p\n", intf, id); 300f26e30ccSAmitkumar Karwar 301f26e30ccSAmitkumar Karwar drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); 302f26e30ccSAmitkumar Karwar if (!drv_data) 303f26e30ccSAmitkumar Karwar return -ENOMEM; 304f26e30ccSAmitkumar Karwar 305f26e30ccSAmitkumar Karwar for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { 3062c95e6c7SKrzysztof Kozlowski struct usb_endpoint_descriptor *ep_desc; 3072c95e6c7SKrzysztof Kozlowski 308f26e30ccSAmitkumar Karwar ep_desc = &intf->cur_altsetting->endpoint[i].desc; 309f26e30ccSAmitkumar Karwar 310f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep && 311f26e30ccSAmitkumar Karwar usb_endpoint_is_bulk_out(ep_desc)) { 312f26e30ccSAmitkumar Karwar drv_data->bulk_tx_ep = ep_desc; 313a5822404SKrzysztof Kozlowski } else if (!drv_data->bulk_rx_ep && 314f26e30ccSAmitkumar Karwar usb_endpoint_is_bulk_in(ep_desc)) { 315f26e30ccSAmitkumar Karwar drv_data->bulk_rx_ep = ep_desc; 316f26e30ccSAmitkumar Karwar } 317f26e30ccSAmitkumar Karwar } 318f26e30ccSAmitkumar Karwar 319f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) 320f26e30ccSAmitkumar Karwar return -ENODEV; 321f26e30ccSAmitkumar Karwar 322f26e30ccSAmitkumar Karwar drv_data->udev = udev; 323f26e30ccSAmitkumar Karwar drv_data->intf = intf; 324f26e30ccSAmitkumar Karwar 325f26e30ccSAmitkumar Karwar INIT_WORK(&drv_data->waker, nfcmrvl_waker); 326f26e30ccSAmitkumar Karwar spin_lock_init(&drv_data->txlock); 327f26e30ccSAmitkumar Karwar 328f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->tx_anchor); 329f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->bulk_anchor); 330f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->deferred); 331f26e30ccSAmitkumar Karwar 33258d34aa6SVincent Cuissard priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops, 3330d1ca88bSJohan Hovold &intf->dev, &config); 334f26e30ccSAmitkumar Karwar if (IS_ERR(priv)) 335f26e30ccSAmitkumar Karwar return PTR_ERR(priv); 336f26e30ccSAmitkumar Karwar 337f26e30ccSAmitkumar Karwar drv_data->priv = priv; 3383194c687SVincent Cuissard drv_data->priv->support_fw_dnld = false; 3393194c687SVincent Cuissard 340f26e30ccSAmitkumar Karwar usb_set_intfdata(intf, drv_data); 341f26e30ccSAmitkumar Karwar 342f26e30ccSAmitkumar Karwar return 0; 343f26e30ccSAmitkumar Karwar } 344f26e30ccSAmitkumar Karwar 345f26e30ccSAmitkumar Karwar static void nfcmrvl_disconnect(struct usb_interface *intf) 346f26e30ccSAmitkumar Karwar { 347f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 348f26e30ccSAmitkumar Karwar 349f26e30ccSAmitkumar Karwar if (!drv_data) 350f26e30ccSAmitkumar Karwar return; 351f26e30ccSAmitkumar Karwar 3523590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 353f26e30ccSAmitkumar Karwar 354f26e30ccSAmitkumar Karwar nfcmrvl_nci_unregister_dev(drv_data->priv); 355f26e30ccSAmitkumar Karwar 356f26e30ccSAmitkumar Karwar usb_set_intfdata(drv_data->intf, NULL); 357f26e30ccSAmitkumar Karwar } 358f26e30ccSAmitkumar Karwar 359f26e30ccSAmitkumar Karwar #ifdef CONFIG_PM 360f26e30ccSAmitkumar Karwar static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) 361f26e30ccSAmitkumar Karwar { 362f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 363f26e30ccSAmitkumar Karwar 3643590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 365f26e30ccSAmitkumar Karwar 366f26e30ccSAmitkumar Karwar if (drv_data->suspend_count++) 367f26e30ccSAmitkumar Karwar return 0; 368f26e30ccSAmitkumar Karwar 369f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 370f26e30ccSAmitkumar Karwar if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { 371f26e30ccSAmitkumar Karwar set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 372f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 373f26e30ccSAmitkumar Karwar } else { 374f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 375f26e30ccSAmitkumar Karwar drv_data->suspend_count--; 376f26e30ccSAmitkumar Karwar return -EBUSY; 377f26e30ccSAmitkumar Karwar } 378f26e30ccSAmitkumar Karwar 379f26e30ccSAmitkumar Karwar nfcmrvl_usb_stop_traffic(drv_data); 380f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->tx_anchor); 381f26e30ccSAmitkumar Karwar 382f26e30ccSAmitkumar Karwar return 0; 383f26e30ccSAmitkumar Karwar } 384f26e30ccSAmitkumar Karwar 385f26e30ccSAmitkumar Karwar static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) 386f26e30ccSAmitkumar Karwar { 387f26e30ccSAmitkumar Karwar struct urb *urb; 388f26e30ccSAmitkumar Karwar int err; 389f26e30ccSAmitkumar Karwar 390f26e30ccSAmitkumar Karwar while ((urb = usb_get_from_anchor(&drv_data->deferred))) { 391f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 392f26e30ccSAmitkumar Karwar if (err) 393f26e30ccSAmitkumar Karwar break; 394f26e30ccSAmitkumar Karwar 395f26e30ccSAmitkumar Karwar drv_data->tx_in_flight++; 396f26e30ccSAmitkumar Karwar } 397f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 398f26e30ccSAmitkumar Karwar } 399f26e30ccSAmitkumar Karwar 400f26e30ccSAmitkumar Karwar static int nfcmrvl_resume(struct usb_interface *intf) 401f26e30ccSAmitkumar Karwar { 402f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 403f26e30ccSAmitkumar Karwar int err = 0; 404f26e30ccSAmitkumar Karwar 4053590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 406f26e30ccSAmitkumar Karwar 407f26e30ccSAmitkumar Karwar if (--drv_data->suspend_count) 408f26e30ccSAmitkumar Karwar return 0; 409f26e30ccSAmitkumar Karwar 410f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 411f26e30ccSAmitkumar Karwar goto done; 412f26e30ccSAmitkumar Karwar 413f26e30ccSAmitkumar Karwar if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { 414f26e30ccSAmitkumar Karwar err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 415f26e30ccSAmitkumar Karwar if (err) { 416f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 417f26e30ccSAmitkumar Karwar goto failed; 418f26e30ccSAmitkumar Karwar } 419f26e30ccSAmitkumar Karwar 420f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 421f26e30ccSAmitkumar Karwar } 422f26e30ccSAmitkumar Karwar 423f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 424f26e30ccSAmitkumar Karwar nfcmrvl_play_deferred(drv_data); 425f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 426f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 427f26e30ccSAmitkumar Karwar 428f26e30ccSAmitkumar Karwar return 0; 429f26e30ccSAmitkumar Karwar 430f26e30ccSAmitkumar Karwar failed: 431f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 432f26e30ccSAmitkumar Karwar done: 433f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 434f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 435f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 436f26e30ccSAmitkumar Karwar 437f26e30ccSAmitkumar Karwar return err; 438f26e30ccSAmitkumar Karwar } 439f26e30ccSAmitkumar Karwar #endif 440f26e30ccSAmitkumar Karwar 441f26e30ccSAmitkumar Karwar static struct usb_driver nfcmrvl_usb_driver = { 442f26e30ccSAmitkumar Karwar .name = "nfcmrvl", 443f26e30ccSAmitkumar Karwar .probe = nfcmrvl_probe, 444f26e30ccSAmitkumar Karwar .disconnect = nfcmrvl_disconnect, 445f26e30ccSAmitkumar Karwar #ifdef CONFIG_PM 446f26e30ccSAmitkumar Karwar .suspend = nfcmrvl_suspend, 447f26e30ccSAmitkumar Karwar .resume = nfcmrvl_resume, 448f26e30ccSAmitkumar Karwar .reset_resume = nfcmrvl_resume, 449f26e30ccSAmitkumar Karwar #endif 450f26e30ccSAmitkumar Karwar .id_table = nfcmrvl_table, 451f26e30ccSAmitkumar Karwar .supports_autosuspend = 1, 452f26e30ccSAmitkumar Karwar .disable_hub_initiated_lpm = 1, 453f26e30ccSAmitkumar Karwar .soft_unbind = 1, 454f26e30ccSAmitkumar Karwar }; 455f26e30ccSAmitkumar Karwar module_usb_driver(nfcmrvl_usb_driver); 456f26e30ccSAmitkumar Karwar 457f26e30ccSAmitkumar Karwar MODULE_AUTHOR("Marvell International Ltd."); 458fb101c0eSVincent Cuissard MODULE_DESCRIPTION("Marvell NFC-over-USB driver"); 459f26e30ccSAmitkumar Karwar MODULE_LICENSE("GPL v2"); 460