15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 290fccb52SAndrzej Pietrasiewicz /* 390fccb52SAndrzej Pietrasiewicz * Copyright (C) 2011 Marvell International Ltd. All rights reserved. 490fccb52SAndrzej Pietrasiewicz */ 590fccb52SAndrzej Pietrasiewicz 690fccb52SAndrzej Pietrasiewicz #include <linux/module.h> 790fccb52SAndrzej Pietrasiewicz #include <linux/dma-mapping.h> 890fccb52SAndrzej Pietrasiewicz #include <linux/dmapool.h> 990fccb52SAndrzej Pietrasiewicz #include <linux/kernel.h> 1090fccb52SAndrzej Pietrasiewicz #include <linux/delay.h> 1190fccb52SAndrzej Pietrasiewicz #include <linux/ioport.h> 1290fccb52SAndrzej Pietrasiewicz #include <linux/sched.h> 1390fccb52SAndrzej Pietrasiewicz #include <linux/slab.h> 1490fccb52SAndrzej Pietrasiewicz #include <linux/errno.h> 1590fccb52SAndrzej Pietrasiewicz #include <linux/timer.h> 1690fccb52SAndrzej Pietrasiewicz #include <linux/list.h> 1790fccb52SAndrzej Pietrasiewicz #include <linux/notifier.h> 1890fccb52SAndrzej Pietrasiewicz #include <linux/interrupt.h> 1990fccb52SAndrzej Pietrasiewicz #include <linux/moduleparam.h> 2090fccb52SAndrzej Pietrasiewicz #include <linux/device.h> 2190fccb52SAndrzej Pietrasiewicz #include <linux/usb/ch9.h> 2290fccb52SAndrzej Pietrasiewicz #include <linux/usb/gadget.h> 2390fccb52SAndrzej Pietrasiewicz #include <linux/pm.h> 2490fccb52SAndrzej Pietrasiewicz #include <linux/io.h> 2590fccb52SAndrzej Pietrasiewicz #include <linux/irq.h> 2690fccb52SAndrzej Pietrasiewicz #include <linux/platform_device.h> 2790fccb52SAndrzej Pietrasiewicz #include <linux/platform_data/mv_usb.h> 2890fccb52SAndrzej Pietrasiewicz #include <linux/clk.h> 2990fccb52SAndrzej Pietrasiewicz 3090fccb52SAndrzej Pietrasiewicz #include "mv_u3d.h" 3190fccb52SAndrzej Pietrasiewicz 3290fccb52SAndrzej Pietrasiewicz #define DRIVER_DESC "Marvell PXA USB3.0 Device Controller driver" 3390fccb52SAndrzej Pietrasiewicz 3490fccb52SAndrzej Pietrasiewicz static const char driver_name[] = "mv_u3d"; 3590fccb52SAndrzej Pietrasiewicz 3690fccb52SAndrzej Pietrasiewicz static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status); 3790fccb52SAndrzej Pietrasiewicz static void mv_u3d_stop_activity(struct mv_u3d *u3d, 3890fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver); 3990fccb52SAndrzej Pietrasiewicz 4090fccb52SAndrzej Pietrasiewicz /* for endpoint 0 operations */ 4190fccb52SAndrzej Pietrasiewicz static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = { 4290fccb52SAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 4390fccb52SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 4490fccb52SAndrzej Pietrasiewicz .bEndpointAddress = 0, 4590fccb52SAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_CONTROL, 4690fccb52SAndrzej Pietrasiewicz .wMaxPacketSize = MV_U3D_EP0_MAX_PKT_SIZE, 4790fccb52SAndrzej Pietrasiewicz }; 4890fccb52SAndrzej Pietrasiewicz 4990fccb52SAndrzej Pietrasiewicz static void mv_u3d_ep0_reset(struct mv_u3d *u3d) 5090fccb52SAndrzej Pietrasiewicz { 5190fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 5290fccb52SAndrzej Pietrasiewicz u32 epxcr; 5390fccb52SAndrzej Pietrasiewicz int i; 5490fccb52SAndrzej Pietrasiewicz 5590fccb52SAndrzej Pietrasiewicz for (i = 0; i < 2; i++) { 5690fccb52SAndrzej Pietrasiewicz ep = &u3d->eps[i]; 5790fccb52SAndrzej Pietrasiewicz ep->u3d = u3d; 5890fccb52SAndrzej Pietrasiewicz 5990fccb52SAndrzej Pietrasiewicz /* ep0 ep context, ep0 in and out share the same ep context */ 6090fccb52SAndrzej Pietrasiewicz ep->ep_context = &u3d->ep_context[1]; 6190fccb52SAndrzej Pietrasiewicz } 6290fccb52SAndrzej Pietrasiewicz 6390fccb52SAndrzej Pietrasiewicz /* reset ep state machine */ 6490fccb52SAndrzej Pietrasiewicz /* reset ep0 out */ 6590fccb52SAndrzej Pietrasiewicz epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); 6690fccb52SAndrzej Pietrasiewicz epxcr |= MV_U3D_EPXCR_EP_INIT; 6790fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); 6890fccb52SAndrzej Pietrasiewicz udelay(5); 6990fccb52SAndrzej Pietrasiewicz epxcr &= ~MV_U3D_EPXCR_EP_INIT; 7090fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); 7190fccb52SAndrzej Pietrasiewicz 7290fccb52SAndrzej Pietrasiewicz epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE 7390fccb52SAndrzej Pietrasiewicz << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) 7490fccb52SAndrzej Pietrasiewicz | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) 7590fccb52SAndrzej Pietrasiewicz | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) 7690fccb52SAndrzej Pietrasiewicz | MV_U3D_EPXCR_EP_TYPE_CONTROL); 7790fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1); 7890fccb52SAndrzej Pietrasiewicz 7990fccb52SAndrzej Pietrasiewicz /* reset ep0 in */ 8090fccb52SAndrzej Pietrasiewicz epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); 8190fccb52SAndrzej Pietrasiewicz epxcr |= MV_U3D_EPXCR_EP_INIT; 8290fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); 8390fccb52SAndrzej Pietrasiewicz udelay(5); 8490fccb52SAndrzej Pietrasiewicz epxcr &= ~MV_U3D_EPXCR_EP_INIT; 8590fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); 8690fccb52SAndrzej Pietrasiewicz 8790fccb52SAndrzej Pietrasiewicz epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE 8890fccb52SAndrzej Pietrasiewicz << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) 8990fccb52SAndrzej Pietrasiewicz | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) 9090fccb52SAndrzej Pietrasiewicz | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) 9190fccb52SAndrzej Pietrasiewicz | MV_U3D_EPXCR_EP_TYPE_CONTROL); 9290fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1); 9390fccb52SAndrzej Pietrasiewicz } 9490fccb52SAndrzej Pietrasiewicz 9590fccb52SAndrzej Pietrasiewicz static void mv_u3d_ep0_stall(struct mv_u3d *u3d) 9690fccb52SAndrzej Pietrasiewicz { 9790fccb52SAndrzej Pietrasiewicz u32 tmp; 9890fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "%s\n", __func__); 9990fccb52SAndrzej Pietrasiewicz 10090fccb52SAndrzej Pietrasiewicz /* set TX and RX to stall */ 10190fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); 10290fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_HALT; 10390fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); 10490fccb52SAndrzej Pietrasiewicz 10590fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); 10690fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_HALT; 10790fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); 10890fccb52SAndrzej Pietrasiewicz 10990fccb52SAndrzej Pietrasiewicz /* update ep0 state */ 11090fccb52SAndrzej Pietrasiewicz u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; 11190fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = MV_U3D_EP_DIR_OUT; 11290fccb52SAndrzej Pietrasiewicz } 11390fccb52SAndrzej Pietrasiewicz 11490fccb52SAndrzej Pietrasiewicz static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, 11590fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *curr_req) 11690fccb52SAndrzej Pietrasiewicz { 11790fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb *curr_trb; 118bd8dc737SMichal Nazarewicz int actual, remaining_length = 0; 11990fccb52SAndrzej Pietrasiewicz int direction, ep_num; 12090fccb52SAndrzej Pietrasiewicz int retval = 0; 12190fccb52SAndrzej Pietrasiewicz u32 tmp, status, length; 12290fccb52SAndrzej Pietrasiewicz 12390fccb52SAndrzej Pietrasiewicz direction = index % 2; 12490fccb52SAndrzej Pietrasiewicz ep_num = index / 2; 12590fccb52SAndrzej Pietrasiewicz 12690fccb52SAndrzej Pietrasiewicz actual = curr_req->req.length; 12790fccb52SAndrzej Pietrasiewicz 12890fccb52SAndrzej Pietrasiewicz while (!list_empty(&curr_req->trb_list)) { 12990fccb52SAndrzej Pietrasiewicz curr_trb = list_entry(curr_req->trb_list.next, 13090fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb, trb_list); 13190fccb52SAndrzej Pietrasiewicz if (!curr_trb->trb_hw->ctrl.own) { 13290fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "%s, TRB own error!\n", 13390fccb52SAndrzej Pietrasiewicz u3d->eps[index].name); 13490fccb52SAndrzej Pietrasiewicz return 1; 13590fccb52SAndrzej Pietrasiewicz } 13690fccb52SAndrzej Pietrasiewicz 13790fccb52SAndrzej Pietrasiewicz curr_trb->trb_hw->ctrl.own = 0; 138bd8dc737SMichal Nazarewicz if (direction == MV_U3D_EP_DIR_OUT) 13990fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo); 140bd8dc737SMichal Nazarewicz else 14190fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo); 14290fccb52SAndrzej Pietrasiewicz 14390fccb52SAndrzej Pietrasiewicz status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT; 14490fccb52SAndrzej Pietrasiewicz length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK; 14590fccb52SAndrzej Pietrasiewicz 14690fccb52SAndrzej Pietrasiewicz if (status == MV_U3D_COMPLETE_SUCCESS || 14790fccb52SAndrzej Pietrasiewicz (status == MV_U3D_COMPLETE_SHORT_PACKET && 14890fccb52SAndrzej Pietrasiewicz direction == MV_U3D_EP_DIR_OUT)) { 14990fccb52SAndrzej Pietrasiewicz remaining_length += length; 15090fccb52SAndrzej Pietrasiewicz actual -= remaining_length; 15190fccb52SAndrzej Pietrasiewicz } else { 15290fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 15390fccb52SAndrzej Pietrasiewicz "complete_tr error: ep=%d %s: error = 0x%x\n", 15490fccb52SAndrzej Pietrasiewicz index >> 1, direction ? "SEND" : "RECV", 15590fccb52SAndrzej Pietrasiewicz status); 15690fccb52SAndrzej Pietrasiewicz retval = -EPROTO; 15790fccb52SAndrzej Pietrasiewicz } 15890fccb52SAndrzej Pietrasiewicz 15990fccb52SAndrzej Pietrasiewicz list_del_init(&curr_trb->trb_list); 16090fccb52SAndrzej Pietrasiewicz } 16190fccb52SAndrzej Pietrasiewicz if (retval) 16290fccb52SAndrzej Pietrasiewicz return retval; 16390fccb52SAndrzej Pietrasiewicz 16490fccb52SAndrzej Pietrasiewicz curr_req->req.actual = actual; 16590fccb52SAndrzej Pietrasiewicz return 0; 16690fccb52SAndrzej Pietrasiewicz } 16790fccb52SAndrzej Pietrasiewicz 16890fccb52SAndrzej Pietrasiewicz /* 16990fccb52SAndrzej Pietrasiewicz * mv_u3d_done() - retire a request; caller blocked irqs 17090fccb52SAndrzej Pietrasiewicz * @status : request status to be set, only works when 17190fccb52SAndrzej Pietrasiewicz * request is still in progress. 17290fccb52SAndrzej Pietrasiewicz */ 17390fccb52SAndrzej Pietrasiewicz static 17490fccb52SAndrzej Pietrasiewicz void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status) 17590fccb52SAndrzej Pietrasiewicz __releases(&ep->udc->lock) 17690fccb52SAndrzej Pietrasiewicz __acquires(&ep->udc->lock) 17790fccb52SAndrzej Pietrasiewicz { 17890fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d; 17990fccb52SAndrzej Pietrasiewicz 18090fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n"); 18190fccb52SAndrzej Pietrasiewicz /* Removed the req from ep queue */ 18290fccb52SAndrzej Pietrasiewicz list_del_init(&req->queue); 18390fccb52SAndrzej Pietrasiewicz 18490fccb52SAndrzej Pietrasiewicz /* req.status should be set as -EINPROGRESS in ep_queue() */ 18590fccb52SAndrzej Pietrasiewicz if (req->req.status == -EINPROGRESS) 18690fccb52SAndrzej Pietrasiewicz req->req.status = status; 18790fccb52SAndrzej Pietrasiewicz else 18890fccb52SAndrzej Pietrasiewicz status = req->req.status; 18990fccb52SAndrzej Pietrasiewicz 19090fccb52SAndrzej Pietrasiewicz /* Free trb for the request */ 19190fccb52SAndrzej Pietrasiewicz if (!req->chain) 19290fccb52SAndrzej Pietrasiewicz dma_pool_free(u3d->trb_pool, 19390fccb52SAndrzej Pietrasiewicz req->trb_head->trb_hw, req->trb_head->trb_dma); 19490fccb52SAndrzej Pietrasiewicz else { 19590fccb52SAndrzej Pietrasiewicz dma_unmap_single(ep->u3d->gadget.dev.parent, 19690fccb52SAndrzej Pietrasiewicz (dma_addr_t)req->trb_head->trb_dma, 19790fccb52SAndrzej Pietrasiewicz req->trb_count * sizeof(struct mv_u3d_trb_hw), 19890fccb52SAndrzej Pietrasiewicz DMA_BIDIRECTIONAL); 19990fccb52SAndrzej Pietrasiewicz kfree(req->trb_head->trb_hw); 20090fccb52SAndrzej Pietrasiewicz } 20190fccb52SAndrzej Pietrasiewicz kfree(req->trb_head); 20290fccb52SAndrzej Pietrasiewicz 20390fccb52SAndrzej Pietrasiewicz usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep)); 20490fccb52SAndrzej Pietrasiewicz 20590fccb52SAndrzej Pietrasiewicz if (status && (status != -ESHUTDOWN)) { 20690fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u", 20790fccb52SAndrzej Pietrasiewicz ep->ep.name, &req->req, status, 20890fccb52SAndrzej Pietrasiewicz req->req.actual, req->req.length); 20990fccb52SAndrzej Pietrasiewicz } 21090fccb52SAndrzej Pietrasiewicz 21190fccb52SAndrzej Pietrasiewicz spin_unlock(&ep->u3d->lock); 212304f7e5eSMichal Sojka 213304f7e5eSMichal Sojka usb_gadget_giveback_request(&ep->ep, &req->req); 21490fccb52SAndrzej Pietrasiewicz 21590fccb52SAndrzej Pietrasiewicz spin_lock(&ep->u3d->lock); 21690fccb52SAndrzej Pietrasiewicz } 21790fccb52SAndrzej Pietrasiewicz 21890fccb52SAndrzej Pietrasiewicz static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req) 21990fccb52SAndrzej Pietrasiewicz { 22090fccb52SAndrzej Pietrasiewicz u32 tmp, direction; 22190fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 22290fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep_context *ep_context; 22390fccb52SAndrzej Pietrasiewicz int retval = 0; 22490fccb52SAndrzej Pietrasiewicz 22590fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 22690fccb52SAndrzej Pietrasiewicz direction = mv_u3d_ep_dir(ep); 22790fccb52SAndrzej Pietrasiewicz 22890fccb52SAndrzej Pietrasiewicz /* ep0 in and out share the same ep context slot 1*/ 22990fccb52SAndrzej Pietrasiewicz if (ep->ep_num == 0) 23090fccb52SAndrzej Pietrasiewicz ep_context = &(u3d->ep_context[1]); 23190fccb52SAndrzej Pietrasiewicz else 23290fccb52SAndrzej Pietrasiewicz ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]); 23390fccb52SAndrzej Pietrasiewicz 23490fccb52SAndrzej Pietrasiewicz /* check if the pipe is empty or not */ 23590fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 23690fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "add trb to non-empty queue!\n"); 23790fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 23890fccb52SAndrzej Pietrasiewicz WARN_ON(1); 23990fccb52SAndrzej Pietrasiewicz } else { 24090fccb52SAndrzej Pietrasiewicz ep_context->rsvd0 = cpu_to_le32(1); 24190fccb52SAndrzej Pietrasiewicz ep_context->rsvd1 = 0; 24290fccb52SAndrzej Pietrasiewicz 24390fccb52SAndrzej Pietrasiewicz /* Configure the trb address and set the DCS bit. 24490fccb52SAndrzej Pietrasiewicz * Both DCS bit and own bit in trb should be set. 24590fccb52SAndrzej Pietrasiewicz */ 24690fccb52SAndrzej Pietrasiewicz ep_context->trb_addr_lo = 24790fccb52SAndrzej Pietrasiewicz cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE); 24890fccb52SAndrzej Pietrasiewicz ep_context->trb_addr_hi = 0; 24990fccb52SAndrzej Pietrasiewicz 25090fccb52SAndrzej Pietrasiewicz /* Ensure that updates to the EP Context will 25190fccb52SAndrzej Pietrasiewicz * occure before Ring Bell. 25290fccb52SAndrzej Pietrasiewicz */ 25390fccb52SAndrzej Pietrasiewicz wmb(); 25490fccb52SAndrzej Pietrasiewicz 25590fccb52SAndrzej Pietrasiewicz /* ring bell the ep */ 25690fccb52SAndrzej Pietrasiewicz if (ep->ep_num == 0) 25790fccb52SAndrzej Pietrasiewicz tmp = 0x1; 25890fccb52SAndrzej Pietrasiewicz else 25990fccb52SAndrzej Pietrasiewicz tmp = ep->ep_num * 2 26090fccb52SAndrzej Pietrasiewicz + ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1); 26190fccb52SAndrzej Pietrasiewicz 26290fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->op_regs->doorbell); 26390fccb52SAndrzej Pietrasiewicz } 26490fccb52SAndrzej Pietrasiewicz return retval; 26590fccb52SAndrzej Pietrasiewicz } 26690fccb52SAndrzej Pietrasiewicz 26790fccb52SAndrzej Pietrasiewicz static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, 26890fccb52SAndrzej Pietrasiewicz unsigned *length, dma_addr_t *dma) 26990fccb52SAndrzej Pietrasiewicz { 27090fccb52SAndrzej Pietrasiewicz u32 temp; 27190fccb52SAndrzej Pietrasiewicz unsigned int direction; 27290fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb *trb; 27390fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb_hw *trb_hw; 27490fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 27590fccb52SAndrzej Pietrasiewicz 27690fccb52SAndrzej Pietrasiewicz /* how big will this transfer be? */ 27790fccb52SAndrzej Pietrasiewicz *length = req->req.length - req->req.actual; 27890fccb52SAndrzej Pietrasiewicz BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); 27990fccb52SAndrzej Pietrasiewicz 28090fccb52SAndrzej Pietrasiewicz u3d = req->ep->u3d; 28190fccb52SAndrzej Pietrasiewicz 28290fccb52SAndrzej Pietrasiewicz trb = kzalloc(sizeof(*trb), GFP_ATOMIC); 28390fccb52SAndrzej Pietrasiewicz if (!trb) 28490fccb52SAndrzej Pietrasiewicz return NULL; 28590fccb52SAndrzej Pietrasiewicz 28690fccb52SAndrzej Pietrasiewicz /* 28790fccb52SAndrzej Pietrasiewicz * Be careful that no _GFP_HIGHMEM is set, 28890fccb52SAndrzej Pietrasiewicz * or we can not use dma_to_virt 28990fccb52SAndrzej Pietrasiewicz * cannot use GFP_KERNEL in spin lock 29090fccb52SAndrzej Pietrasiewicz */ 29190fccb52SAndrzej Pietrasiewicz trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); 29290fccb52SAndrzej Pietrasiewicz if (!trb_hw) { 29390fccb52SAndrzej Pietrasiewicz kfree(trb); 29490fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 29590fccb52SAndrzej Pietrasiewicz "%s, dma_pool_alloc fail\n", __func__); 29690fccb52SAndrzej Pietrasiewicz return NULL; 29790fccb52SAndrzej Pietrasiewicz } 29890fccb52SAndrzej Pietrasiewicz trb->trb_dma = *dma; 29990fccb52SAndrzej Pietrasiewicz trb->trb_hw = trb_hw; 30090fccb52SAndrzej Pietrasiewicz 30190fccb52SAndrzej Pietrasiewicz /* initialize buffer page pointers */ 30290fccb52SAndrzej Pietrasiewicz temp = (u32)(req->req.dma + req->req.actual); 30390fccb52SAndrzej Pietrasiewicz 30490fccb52SAndrzej Pietrasiewicz trb_hw->buf_addr_lo = cpu_to_le32(temp); 30590fccb52SAndrzej Pietrasiewicz trb_hw->buf_addr_hi = 0; 30690fccb52SAndrzej Pietrasiewicz trb_hw->trb_len = cpu_to_le32(*length); 30790fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.own = 1; 30890fccb52SAndrzej Pietrasiewicz 30990fccb52SAndrzej Pietrasiewicz if (req->ep->ep_num == 0) 31090fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.type = TYPE_DATA; 31190fccb52SAndrzej Pietrasiewicz else 31290fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.type = TYPE_NORMAL; 31390fccb52SAndrzej Pietrasiewicz 31490fccb52SAndrzej Pietrasiewicz req->req.actual += *length; 31590fccb52SAndrzej Pietrasiewicz 31690fccb52SAndrzej Pietrasiewicz direction = mv_u3d_ep_dir(req->ep); 31790fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_IN) 31890fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.dir = 1; 31990fccb52SAndrzej Pietrasiewicz else 32090fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.dir = 0; 32190fccb52SAndrzej Pietrasiewicz 32290fccb52SAndrzej Pietrasiewicz /* Enable interrupt for the last trb of a request */ 32390fccb52SAndrzej Pietrasiewicz if (!req->req.no_interrupt) 32490fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.ioc = 1; 32590fccb52SAndrzej Pietrasiewicz 32690fccb52SAndrzej Pietrasiewicz trb_hw->ctrl.chain = 0; 32790fccb52SAndrzej Pietrasiewicz 32890fccb52SAndrzej Pietrasiewicz wmb(); 32990fccb52SAndrzej Pietrasiewicz return trb; 33090fccb52SAndrzej Pietrasiewicz } 33190fccb52SAndrzej Pietrasiewicz 33290fccb52SAndrzej Pietrasiewicz static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length, 33390fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb *trb, int *is_last) 33490fccb52SAndrzej Pietrasiewicz { 33590fccb52SAndrzej Pietrasiewicz u32 temp; 33690fccb52SAndrzej Pietrasiewicz unsigned int direction; 33790fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 33890fccb52SAndrzej Pietrasiewicz 33990fccb52SAndrzej Pietrasiewicz /* how big will this transfer be? */ 34090fccb52SAndrzej Pietrasiewicz *length = min(req->req.length - req->req.actual, 34190fccb52SAndrzej Pietrasiewicz (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); 34290fccb52SAndrzej Pietrasiewicz 34390fccb52SAndrzej Pietrasiewicz u3d = req->ep->u3d; 34490fccb52SAndrzej Pietrasiewicz 34590fccb52SAndrzej Pietrasiewicz trb->trb_dma = 0; 34690fccb52SAndrzej Pietrasiewicz 34790fccb52SAndrzej Pietrasiewicz /* initialize buffer page pointers */ 34890fccb52SAndrzej Pietrasiewicz temp = (u32)(req->req.dma + req->req.actual); 34990fccb52SAndrzej Pietrasiewicz 35090fccb52SAndrzej Pietrasiewicz trb->trb_hw->buf_addr_lo = cpu_to_le32(temp); 35190fccb52SAndrzej Pietrasiewicz trb->trb_hw->buf_addr_hi = 0; 35290fccb52SAndrzej Pietrasiewicz trb->trb_hw->trb_len = cpu_to_le32(*length); 35390fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.own = 1; 35490fccb52SAndrzej Pietrasiewicz 35590fccb52SAndrzej Pietrasiewicz if (req->ep->ep_num == 0) 35690fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.type = TYPE_DATA; 35790fccb52SAndrzej Pietrasiewicz else 35890fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.type = TYPE_NORMAL; 35990fccb52SAndrzej Pietrasiewicz 36090fccb52SAndrzej Pietrasiewicz req->req.actual += *length; 36190fccb52SAndrzej Pietrasiewicz 36290fccb52SAndrzej Pietrasiewicz direction = mv_u3d_ep_dir(req->ep); 36390fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_IN) 36490fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.dir = 1; 36590fccb52SAndrzej Pietrasiewicz else 36690fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.dir = 0; 36790fccb52SAndrzej Pietrasiewicz 36890fccb52SAndrzej Pietrasiewicz /* zlp is needed if req->req.zero is set */ 36990fccb52SAndrzej Pietrasiewicz if (req->req.zero) { 37090fccb52SAndrzej Pietrasiewicz if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) 37190fccb52SAndrzej Pietrasiewicz *is_last = 1; 37290fccb52SAndrzej Pietrasiewicz else 37390fccb52SAndrzej Pietrasiewicz *is_last = 0; 37490fccb52SAndrzej Pietrasiewicz } else if (req->req.length == req->req.actual) 37590fccb52SAndrzej Pietrasiewicz *is_last = 1; 37690fccb52SAndrzej Pietrasiewicz else 37790fccb52SAndrzej Pietrasiewicz *is_last = 0; 37890fccb52SAndrzej Pietrasiewicz 37990fccb52SAndrzej Pietrasiewicz /* Enable interrupt for the last trb of a request */ 38090fccb52SAndrzej Pietrasiewicz if (*is_last && !req->req.no_interrupt) 38190fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.ioc = 1; 38290fccb52SAndrzej Pietrasiewicz 38390fccb52SAndrzej Pietrasiewicz if (*is_last) 38490fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.chain = 0; 38590fccb52SAndrzej Pietrasiewicz else { 38690fccb52SAndrzej Pietrasiewicz trb->trb_hw->ctrl.chain = 1; 38790fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "chain trb\n"); 38890fccb52SAndrzej Pietrasiewicz } 38990fccb52SAndrzej Pietrasiewicz 39090fccb52SAndrzej Pietrasiewicz wmb(); 39190fccb52SAndrzej Pietrasiewicz 39290fccb52SAndrzej Pietrasiewicz return 0; 39390fccb52SAndrzej Pietrasiewicz } 39490fccb52SAndrzej Pietrasiewicz 39590fccb52SAndrzej Pietrasiewicz /* generate TRB linked list for a request 39690fccb52SAndrzej Pietrasiewicz * usb controller only supports continous trb chain, 39790fccb52SAndrzej Pietrasiewicz * that trb structure physical address should be continous. 39890fccb52SAndrzej Pietrasiewicz */ 39990fccb52SAndrzej Pietrasiewicz static int mv_u3d_req_to_trb(struct mv_u3d_req *req) 40090fccb52SAndrzej Pietrasiewicz { 40190fccb52SAndrzej Pietrasiewicz unsigned count; 40290fccb52SAndrzej Pietrasiewicz int is_last; 40390fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb *trb; 40490fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb_hw *trb_hw; 40590fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 40690fccb52SAndrzej Pietrasiewicz dma_addr_t dma; 40790fccb52SAndrzej Pietrasiewicz unsigned length; 40890fccb52SAndrzej Pietrasiewicz unsigned trb_num; 40990fccb52SAndrzej Pietrasiewicz 41090fccb52SAndrzej Pietrasiewicz u3d = req->ep->u3d; 41190fccb52SAndrzej Pietrasiewicz 41290fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&req->trb_list); 41390fccb52SAndrzej Pietrasiewicz 41490fccb52SAndrzej Pietrasiewicz length = req->req.length - req->req.actual; 41590fccb52SAndrzej Pietrasiewicz /* normally the request transfer length is less than 16KB. 41690fccb52SAndrzej Pietrasiewicz * we use buil_trb_one() to optimize it. 41790fccb52SAndrzej Pietrasiewicz */ 41890fccb52SAndrzej Pietrasiewicz if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) { 41990fccb52SAndrzej Pietrasiewicz trb = mv_u3d_build_trb_one(req, &count, &dma); 42090fccb52SAndrzej Pietrasiewicz list_add_tail(&trb->trb_list, &req->trb_list); 42190fccb52SAndrzej Pietrasiewicz req->trb_head = trb; 42290fccb52SAndrzej Pietrasiewicz req->trb_count = 1; 42390fccb52SAndrzej Pietrasiewicz req->chain = 0; 42490fccb52SAndrzej Pietrasiewicz } else { 42590fccb52SAndrzej Pietrasiewicz trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER; 42690fccb52SAndrzej Pietrasiewicz if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER) 42790fccb52SAndrzej Pietrasiewicz trb_num++; 42890fccb52SAndrzej Pietrasiewicz 42990fccb52SAndrzej Pietrasiewicz trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC); 43090fccb52SAndrzej Pietrasiewicz if (!trb) 43190fccb52SAndrzej Pietrasiewicz return -ENOMEM; 43290fccb52SAndrzej Pietrasiewicz 43390fccb52SAndrzej Pietrasiewicz trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); 43490fccb52SAndrzej Pietrasiewicz if (!trb_hw) { 43590fccb52SAndrzej Pietrasiewicz kfree(trb); 43690fccb52SAndrzej Pietrasiewicz return -ENOMEM; 43790fccb52SAndrzej Pietrasiewicz } 43890fccb52SAndrzej Pietrasiewicz 43990fccb52SAndrzej Pietrasiewicz do { 44090fccb52SAndrzej Pietrasiewicz trb->trb_hw = trb_hw; 44190fccb52SAndrzej Pietrasiewicz if (mv_u3d_build_trb_chain(req, &count, 44290fccb52SAndrzej Pietrasiewicz trb, &is_last)) { 44390fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 44490fccb52SAndrzej Pietrasiewicz "%s, mv_u3d_build_trb_chain fail\n", 44590fccb52SAndrzej Pietrasiewicz __func__); 44690fccb52SAndrzej Pietrasiewicz return -EIO; 44790fccb52SAndrzej Pietrasiewicz } 44890fccb52SAndrzej Pietrasiewicz 44990fccb52SAndrzej Pietrasiewicz list_add_tail(&trb->trb_list, &req->trb_list); 45090fccb52SAndrzej Pietrasiewicz req->trb_count++; 45190fccb52SAndrzej Pietrasiewicz trb++; 45290fccb52SAndrzej Pietrasiewicz trb_hw++; 45390fccb52SAndrzej Pietrasiewicz } while (!is_last); 45490fccb52SAndrzej Pietrasiewicz 45590fccb52SAndrzej Pietrasiewicz req->trb_head = list_entry(req->trb_list.next, 45690fccb52SAndrzej Pietrasiewicz struct mv_u3d_trb, trb_list); 45790fccb52SAndrzej Pietrasiewicz req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent, 45890fccb52SAndrzej Pietrasiewicz req->trb_head->trb_hw, 45990fccb52SAndrzej Pietrasiewicz trb_num * sizeof(*trb_hw), 46090fccb52SAndrzej Pietrasiewicz DMA_BIDIRECTIONAL); 4616ffd6515SAlexey Khoroshilov if (dma_mapping_error(u3d->gadget.dev.parent, 4626ffd6515SAlexey Khoroshilov req->trb_head->trb_dma)) { 4636ffd6515SAlexey Khoroshilov kfree(req->trb_head->trb_hw); 4646ffd6515SAlexey Khoroshilov kfree(req->trb_head); 4656ffd6515SAlexey Khoroshilov return -EFAULT; 4666ffd6515SAlexey Khoroshilov } 46790fccb52SAndrzej Pietrasiewicz 46890fccb52SAndrzej Pietrasiewicz req->chain = 1; 46990fccb52SAndrzej Pietrasiewicz } 47090fccb52SAndrzej Pietrasiewicz 47190fccb52SAndrzej Pietrasiewicz return 0; 47290fccb52SAndrzej Pietrasiewicz } 47390fccb52SAndrzej Pietrasiewicz 47490fccb52SAndrzej Pietrasiewicz static int 47590fccb52SAndrzej Pietrasiewicz mv_u3d_start_queue(struct mv_u3d_ep *ep) 47690fccb52SAndrzej Pietrasiewicz { 47790fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = ep->u3d; 47890fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *req; 47990fccb52SAndrzej Pietrasiewicz int ret; 48090fccb52SAndrzej Pietrasiewicz 48190fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->req_list) && !ep->processing) 48290fccb52SAndrzej Pietrasiewicz req = list_entry(ep->req_list.next, struct mv_u3d_req, list); 48390fccb52SAndrzej Pietrasiewicz else 48490fccb52SAndrzej Pietrasiewicz return 0; 48590fccb52SAndrzej Pietrasiewicz 48690fccb52SAndrzej Pietrasiewicz ep->processing = 1; 48790fccb52SAndrzej Pietrasiewicz 48890fccb52SAndrzej Pietrasiewicz /* set up dma mapping */ 48990fccb52SAndrzej Pietrasiewicz ret = usb_gadget_map_request(&u3d->gadget, &req->req, 49090fccb52SAndrzej Pietrasiewicz mv_u3d_ep_dir(ep)); 49190fccb52SAndrzej Pietrasiewicz if (ret) 492dc9ef588SAlexey Khoroshilov goto break_processing; 49390fccb52SAndrzej Pietrasiewicz 49490fccb52SAndrzej Pietrasiewicz req->req.status = -EINPROGRESS; 49590fccb52SAndrzej Pietrasiewicz req->req.actual = 0; 49690fccb52SAndrzej Pietrasiewicz req->trb_count = 0; 49790fccb52SAndrzej Pietrasiewicz 498dc9ef588SAlexey Khoroshilov /* build trbs */ 499dc9ef588SAlexey Khoroshilov ret = mv_u3d_req_to_trb(req); 50090fccb52SAndrzej Pietrasiewicz if (ret) { 50190fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__); 502dc9ef588SAlexey Khoroshilov goto break_processing; 50390fccb52SAndrzej Pietrasiewicz } 50490fccb52SAndrzej Pietrasiewicz 505dc9ef588SAlexey Khoroshilov /* and push them to device queue */ 506dc9ef588SAlexey Khoroshilov ret = mv_u3d_queue_trb(ep, req); 507dc9ef588SAlexey Khoroshilov if (ret) 508dc9ef588SAlexey Khoroshilov goto break_processing; 509dc9ef588SAlexey Khoroshilov 51090fccb52SAndrzej Pietrasiewicz /* irq handler advances the queue */ 51190fccb52SAndrzej Pietrasiewicz list_add_tail(&req->queue, &ep->queue); 51290fccb52SAndrzej Pietrasiewicz 51390fccb52SAndrzej Pietrasiewicz return 0; 514dc9ef588SAlexey Khoroshilov 515dc9ef588SAlexey Khoroshilov break_processing: 516dc9ef588SAlexey Khoroshilov ep->processing = 0; 517dc9ef588SAlexey Khoroshilov return ret; 51890fccb52SAndrzej Pietrasiewicz } 51990fccb52SAndrzej Pietrasiewicz 52090fccb52SAndrzej Pietrasiewicz static int mv_u3d_ep_enable(struct usb_ep *_ep, 52190fccb52SAndrzej Pietrasiewicz const struct usb_endpoint_descriptor *desc) 52290fccb52SAndrzej Pietrasiewicz { 52390fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 52490fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 52590fccb52SAndrzej Pietrasiewicz u16 max = 0; 52690fccb52SAndrzej Pietrasiewicz unsigned maxburst = 0; 52790fccb52SAndrzej Pietrasiewicz u32 epxcr, direction; 52890fccb52SAndrzej Pietrasiewicz 52990fccb52SAndrzej Pietrasiewicz if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) 53090fccb52SAndrzej Pietrasiewicz return -EINVAL; 53190fccb52SAndrzej Pietrasiewicz 53290fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct mv_u3d_ep, ep); 53390fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 53490fccb52SAndrzej Pietrasiewicz 53590fccb52SAndrzej Pietrasiewicz if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) 53690fccb52SAndrzej Pietrasiewicz return -ESHUTDOWN; 53790fccb52SAndrzej Pietrasiewicz 53890fccb52SAndrzej Pietrasiewicz direction = mv_u3d_ep_dir(ep); 53990fccb52SAndrzej Pietrasiewicz max = le16_to_cpu(desc->wMaxPacketSize); 54090fccb52SAndrzej Pietrasiewicz 54190fccb52SAndrzej Pietrasiewicz if (!_ep->maxburst) 54290fccb52SAndrzej Pietrasiewicz _ep->maxburst = 1; 54390fccb52SAndrzej Pietrasiewicz maxburst = _ep->maxburst; 54490fccb52SAndrzej Pietrasiewicz 54590fccb52SAndrzej Pietrasiewicz /* Set the max burst size */ 54690fccb52SAndrzej Pietrasiewicz switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { 54790fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_BULK: 54890fccb52SAndrzej Pietrasiewicz if (maxburst > 16) { 54990fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 55090fccb52SAndrzej Pietrasiewicz "max burst should not be greater " 55190fccb52SAndrzej Pietrasiewicz "than 16 on bulk ep\n"); 55290fccb52SAndrzej Pietrasiewicz maxburst = 1; 55390fccb52SAndrzej Pietrasiewicz _ep->maxburst = maxburst; 55490fccb52SAndrzej Pietrasiewicz } 55590fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 55690fccb52SAndrzej Pietrasiewicz "maxburst: %d on bulk %s\n", maxburst, ep->name); 55790fccb52SAndrzej Pietrasiewicz break; 55890fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_CONTROL: 55990fccb52SAndrzej Pietrasiewicz /* control transfer only supports maxburst as one */ 56090fccb52SAndrzej Pietrasiewicz maxburst = 1; 56190fccb52SAndrzej Pietrasiewicz _ep->maxburst = maxburst; 56290fccb52SAndrzej Pietrasiewicz break; 56390fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_INT: 56490fccb52SAndrzej Pietrasiewicz if (maxburst != 1) { 56590fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 56690fccb52SAndrzej Pietrasiewicz "max burst should be 1 on int ep " 56790fccb52SAndrzej Pietrasiewicz "if transfer size is not 1024\n"); 56890fccb52SAndrzej Pietrasiewicz maxburst = 1; 56990fccb52SAndrzej Pietrasiewicz _ep->maxburst = maxburst; 57090fccb52SAndrzej Pietrasiewicz } 57190fccb52SAndrzej Pietrasiewicz break; 57290fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_ISOC: 57390fccb52SAndrzej Pietrasiewicz if (maxburst != 1) { 57490fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 57590fccb52SAndrzej Pietrasiewicz "max burst should be 1 on isoc ep " 57690fccb52SAndrzej Pietrasiewicz "if transfer size is not 1024\n"); 57790fccb52SAndrzej Pietrasiewicz maxburst = 1; 57890fccb52SAndrzej Pietrasiewicz _ep->maxburst = maxburst; 57990fccb52SAndrzej Pietrasiewicz } 58090fccb52SAndrzej Pietrasiewicz break; 58190fccb52SAndrzej Pietrasiewicz default: 58290fccb52SAndrzej Pietrasiewicz goto en_done; 58390fccb52SAndrzej Pietrasiewicz } 58490fccb52SAndrzej Pietrasiewicz 58590fccb52SAndrzej Pietrasiewicz ep->ep.maxpacket = max; 58690fccb52SAndrzej Pietrasiewicz ep->ep.desc = desc; 58790fccb52SAndrzej Pietrasiewicz ep->enabled = 1; 58890fccb52SAndrzej Pietrasiewicz 58990fccb52SAndrzej Pietrasiewicz /* Enable the endpoint for Rx or Tx and set the endpoint type */ 59090fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_OUT) { 59190fccb52SAndrzej Pietrasiewicz epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 59290fccb52SAndrzej Pietrasiewicz epxcr |= MV_U3D_EPXCR_EP_INIT; 59390fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 59490fccb52SAndrzej Pietrasiewicz udelay(5); 59590fccb52SAndrzej Pietrasiewicz epxcr &= ~MV_U3D_EPXCR_EP_INIT; 59690fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 59790fccb52SAndrzej Pietrasiewicz 59890fccb52SAndrzej Pietrasiewicz epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) 59990fccb52SAndrzej Pietrasiewicz | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) 60090fccb52SAndrzej Pietrasiewicz | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) 60190fccb52SAndrzej Pietrasiewicz | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); 60290fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); 60390fccb52SAndrzej Pietrasiewicz } else { 60490fccb52SAndrzej Pietrasiewicz epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 60590fccb52SAndrzej Pietrasiewicz epxcr |= MV_U3D_EPXCR_EP_INIT; 60690fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 60790fccb52SAndrzej Pietrasiewicz udelay(5); 60890fccb52SAndrzej Pietrasiewicz epxcr &= ~MV_U3D_EPXCR_EP_INIT; 60990fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 61090fccb52SAndrzej Pietrasiewicz 61190fccb52SAndrzej Pietrasiewicz epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) 61290fccb52SAndrzej Pietrasiewicz | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) 61390fccb52SAndrzej Pietrasiewicz | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) 61490fccb52SAndrzej Pietrasiewicz | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); 61590fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); 61690fccb52SAndrzej Pietrasiewicz } 61790fccb52SAndrzej Pietrasiewicz 61890fccb52SAndrzej Pietrasiewicz return 0; 61990fccb52SAndrzej Pietrasiewicz en_done: 62090fccb52SAndrzej Pietrasiewicz return -EINVAL; 62190fccb52SAndrzej Pietrasiewicz } 62290fccb52SAndrzej Pietrasiewicz 62390fccb52SAndrzej Pietrasiewicz static int mv_u3d_ep_disable(struct usb_ep *_ep) 62490fccb52SAndrzej Pietrasiewicz { 62590fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 62690fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 62790fccb52SAndrzej Pietrasiewicz u32 epxcr, direction; 62890fccb52SAndrzej Pietrasiewicz unsigned long flags; 62990fccb52SAndrzej Pietrasiewicz 63090fccb52SAndrzej Pietrasiewicz if (!_ep) 63190fccb52SAndrzej Pietrasiewicz return -EINVAL; 63290fccb52SAndrzej Pietrasiewicz 63390fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct mv_u3d_ep, ep); 63490fccb52SAndrzej Pietrasiewicz if (!ep->ep.desc) 63590fccb52SAndrzej Pietrasiewicz return -EINVAL; 63690fccb52SAndrzej Pietrasiewicz 63790fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 63890fccb52SAndrzej Pietrasiewicz 63990fccb52SAndrzej Pietrasiewicz direction = mv_u3d_ep_dir(ep); 64090fccb52SAndrzej Pietrasiewicz 64190fccb52SAndrzej Pietrasiewicz /* nuke all pending requests (does flush) */ 64290fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&u3d->lock, flags); 64390fccb52SAndrzej Pietrasiewicz mv_u3d_nuke(ep, -ESHUTDOWN); 64490fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&u3d->lock, flags); 64590fccb52SAndrzej Pietrasiewicz 64690fccb52SAndrzej Pietrasiewicz /* Disable the endpoint for Rx or Tx and reset the endpoint type */ 64790fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_OUT) { 64890fccb52SAndrzej Pietrasiewicz epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); 64990fccb52SAndrzej Pietrasiewicz epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) 65090fccb52SAndrzej Pietrasiewicz | USB_ENDPOINT_XFERTYPE_MASK); 65190fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); 65290fccb52SAndrzej Pietrasiewicz } else { 65390fccb52SAndrzej Pietrasiewicz epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1); 65490fccb52SAndrzej Pietrasiewicz epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) 65590fccb52SAndrzej Pietrasiewicz | USB_ENDPOINT_XFERTYPE_MASK); 65690fccb52SAndrzej Pietrasiewicz iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); 65790fccb52SAndrzej Pietrasiewicz } 65890fccb52SAndrzej Pietrasiewicz 65990fccb52SAndrzej Pietrasiewicz ep->enabled = 0; 66090fccb52SAndrzej Pietrasiewicz 66190fccb52SAndrzej Pietrasiewicz ep->ep.desc = NULL; 66290fccb52SAndrzej Pietrasiewicz return 0; 66390fccb52SAndrzej Pietrasiewicz } 66490fccb52SAndrzej Pietrasiewicz 66590fccb52SAndrzej Pietrasiewicz static struct usb_request * 66690fccb52SAndrzej Pietrasiewicz mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) 66790fccb52SAndrzej Pietrasiewicz { 66890fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *req = NULL; 66990fccb52SAndrzej Pietrasiewicz 67090fccb52SAndrzej Pietrasiewicz req = kzalloc(sizeof *req, gfp_flags); 67190fccb52SAndrzej Pietrasiewicz if (!req) 67290fccb52SAndrzej Pietrasiewicz return NULL; 67390fccb52SAndrzej Pietrasiewicz 67490fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&req->queue); 67590fccb52SAndrzej Pietrasiewicz 67690fccb52SAndrzej Pietrasiewicz return &req->req; 67790fccb52SAndrzej Pietrasiewicz } 67890fccb52SAndrzej Pietrasiewicz 67990fccb52SAndrzej Pietrasiewicz static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req) 68090fccb52SAndrzej Pietrasiewicz { 68190fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req); 68290fccb52SAndrzej Pietrasiewicz 68390fccb52SAndrzej Pietrasiewicz kfree(req); 68490fccb52SAndrzej Pietrasiewicz } 68590fccb52SAndrzej Pietrasiewicz 68690fccb52SAndrzej Pietrasiewicz static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep) 68790fccb52SAndrzej Pietrasiewicz { 68890fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 68990fccb52SAndrzej Pietrasiewicz u32 direction; 69090fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep); 69190fccb52SAndrzej Pietrasiewicz unsigned int loops; 69290fccb52SAndrzej Pietrasiewicz u32 tmp; 69390fccb52SAndrzej Pietrasiewicz 69490fccb52SAndrzej Pietrasiewicz /* if endpoint is not enabled, cannot flush endpoint */ 69590fccb52SAndrzej Pietrasiewicz if (!ep->enabled) 69690fccb52SAndrzej Pietrasiewicz return; 69790fccb52SAndrzej Pietrasiewicz 69890fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 69990fccb52SAndrzej Pietrasiewicz direction = mv_u3d_ep_dir(ep); 70090fccb52SAndrzej Pietrasiewicz 70190fccb52SAndrzej Pietrasiewicz /* ep0 need clear bit after flushing fifo. */ 70290fccb52SAndrzej Pietrasiewicz if (!ep->ep_num) { 70390fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_OUT) { 70490fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); 70590fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_FLUSH; 70690fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); 70790fccb52SAndrzej Pietrasiewicz udelay(10); 70890fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_EPXCR_EP_FLUSH; 70990fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); 71090fccb52SAndrzej Pietrasiewicz } else { 71190fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); 71290fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_FLUSH; 71390fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); 71490fccb52SAndrzej Pietrasiewicz udelay(10); 71590fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_EPXCR_EP_FLUSH; 71690fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); 71790fccb52SAndrzej Pietrasiewicz } 71890fccb52SAndrzej Pietrasiewicz return; 71990fccb52SAndrzej Pietrasiewicz } 72090fccb52SAndrzej Pietrasiewicz 72190fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_OUT) { 72290fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 72390fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_FLUSH; 72490fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 72590fccb52SAndrzej Pietrasiewicz 72690fccb52SAndrzej Pietrasiewicz /* Wait until flushing completed */ 72790fccb52SAndrzej Pietrasiewicz loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); 72890fccb52SAndrzej Pietrasiewicz while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) & 72990fccb52SAndrzej Pietrasiewicz MV_U3D_EPXCR_EP_FLUSH) { 73090fccb52SAndrzej Pietrasiewicz /* 73190fccb52SAndrzej Pietrasiewicz * EP_FLUSH bit should be cleared to indicate this 73290fccb52SAndrzej Pietrasiewicz * operation is complete 73390fccb52SAndrzej Pietrasiewicz */ 73490fccb52SAndrzej Pietrasiewicz if (loops == 0) { 73590fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 73690fccb52SAndrzej Pietrasiewicz "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, 73790fccb52SAndrzej Pietrasiewicz direction ? "in" : "out"); 73890fccb52SAndrzej Pietrasiewicz return; 73990fccb52SAndrzej Pietrasiewicz } 74090fccb52SAndrzej Pietrasiewicz loops--; 74190fccb52SAndrzej Pietrasiewicz udelay(LOOPS_USEC); 74290fccb52SAndrzej Pietrasiewicz } 74390fccb52SAndrzej Pietrasiewicz } else { /* EP_DIR_IN */ 74490fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 74590fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_FLUSH; 74690fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 74790fccb52SAndrzej Pietrasiewicz 74890fccb52SAndrzej Pietrasiewicz /* Wait until flushing completed */ 74990fccb52SAndrzej Pietrasiewicz loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); 75090fccb52SAndrzej Pietrasiewicz while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) & 75190fccb52SAndrzej Pietrasiewicz MV_U3D_EPXCR_EP_FLUSH) { 75290fccb52SAndrzej Pietrasiewicz /* 75390fccb52SAndrzej Pietrasiewicz * EP_FLUSH bit should be cleared to indicate this 75490fccb52SAndrzej Pietrasiewicz * operation is complete 75590fccb52SAndrzej Pietrasiewicz */ 75690fccb52SAndrzej Pietrasiewicz if (loops == 0) { 75790fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 75890fccb52SAndrzej Pietrasiewicz "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, 75990fccb52SAndrzej Pietrasiewicz direction ? "in" : "out"); 76090fccb52SAndrzej Pietrasiewicz return; 76190fccb52SAndrzej Pietrasiewicz } 76290fccb52SAndrzej Pietrasiewicz loops--; 76390fccb52SAndrzej Pietrasiewicz udelay(LOOPS_USEC); 76490fccb52SAndrzej Pietrasiewicz } 76590fccb52SAndrzej Pietrasiewicz } 76690fccb52SAndrzej Pietrasiewicz } 76790fccb52SAndrzej Pietrasiewicz 76890fccb52SAndrzej Pietrasiewicz /* queues (submits) an I/O request to an endpoint */ 76990fccb52SAndrzej Pietrasiewicz static int 77090fccb52SAndrzej Pietrasiewicz mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) 77190fccb52SAndrzej Pietrasiewicz { 77290fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 77390fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *req; 77490fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 77590fccb52SAndrzej Pietrasiewicz unsigned long flags; 77690fccb52SAndrzej Pietrasiewicz int is_first_req = 0; 77790fccb52SAndrzej Pietrasiewicz 77890fccb52SAndrzej Pietrasiewicz if (unlikely(!_ep || !_req)) 77990fccb52SAndrzej Pietrasiewicz return -EINVAL; 78090fccb52SAndrzej Pietrasiewicz 78190fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct mv_u3d_ep, ep); 78290fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 78390fccb52SAndrzej Pietrasiewicz 78490fccb52SAndrzej Pietrasiewicz req = container_of(_req, struct mv_u3d_req, req); 78590fccb52SAndrzej Pietrasiewicz 78690fccb52SAndrzej Pietrasiewicz if (!ep->ep_num 78790fccb52SAndrzej Pietrasiewicz && u3d->ep0_state == MV_U3D_STATUS_STAGE 78890fccb52SAndrzej Pietrasiewicz && !_req->length) { 78990fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "ep0 status stage\n"); 79090fccb52SAndrzej Pietrasiewicz u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; 79190fccb52SAndrzej Pietrasiewicz return 0; 79290fccb52SAndrzej Pietrasiewicz } 79390fccb52SAndrzej Pietrasiewicz 79490fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "%s: %s, req: 0x%p\n", 79590fccb52SAndrzej Pietrasiewicz __func__, _ep->name, req); 79690fccb52SAndrzej Pietrasiewicz 79790fccb52SAndrzej Pietrasiewicz /* catch various bogus parameters */ 79890fccb52SAndrzej Pietrasiewicz if (!req->req.complete || !req->req.buf 79990fccb52SAndrzej Pietrasiewicz || !list_empty(&req->queue)) { 80090fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 80190fccb52SAndrzej Pietrasiewicz "%s, bad params, _req: 0x%p," 80290fccb52SAndrzej Pietrasiewicz "req->req.complete: 0x%p, req->req.buf: 0x%p," 80390fccb52SAndrzej Pietrasiewicz "list_empty: 0x%x\n", 80490fccb52SAndrzej Pietrasiewicz __func__, _req, 80590fccb52SAndrzej Pietrasiewicz req->req.complete, req->req.buf, 80690fccb52SAndrzej Pietrasiewicz list_empty(&req->queue)); 80790fccb52SAndrzej Pietrasiewicz return -EINVAL; 80890fccb52SAndrzej Pietrasiewicz } 80990fccb52SAndrzej Pietrasiewicz if (unlikely(!ep->ep.desc)) { 81090fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "%s, bad ep\n", __func__); 81190fccb52SAndrzej Pietrasiewicz return -EINVAL; 81290fccb52SAndrzej Pietrasiewicz } 81390fccb52SAndrzej Pietrasiewicz if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { 81490fccb52SAndrzej Pietrasiewicz if (req->req.length > ep->ep.maxpacket) 81590fccb52SAndrzej Pietrasiewicz return -EMSGSIZE; 81690fccb52SAndrzej Pietrasiewicz } 81790fccb52SAndrzej Pietrasiewicz 81890fccb52SAndrzej Pietrasiewicz if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) { 81990fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 82090fccb52SAndrzej Pietrasiewicz "bad params of driver/speed\n"); 82190fccb52SAndrzej Pietrasiewicz return -ESHUTDOWN; 82290fccb52SAndrzej Pietrasiewicz } 82390fccb52SAndrzej Pietrasiewicz 82490fccb52SAndrzej Pietrasiewicz req->ep = ep; 82590fccb52SAndrzej Pietrasiewicz 82690fccb52SAndrzej Pietrasiewicz /* Software list handles usb request. */ 82790fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->req_lock, flags); 82890fccb52SAndrzej Pietrasiewicz is_first_req = list_empty(&ep->req_list); 82990fccb52SAndrzej Pietrasiewicz list_add_tail(&req->list, &ep->req_list); 83090fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->req_lock, flags); 83190fccb52SAndrzej Pietrasiewicz if (!is_first_req) { 83290fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "list is not empty\n"); 83390fccb52SAndrzej Pietrasiewicz return 0; 83490fccb52SAndrzej Pietrasiewicz } 83590fccb52SAndrzej Pietrasiewicz 83690fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n"); 83790fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&u3d->lock, flags); 83890fccb52SAndrzej Pietrasiewicz mv_u3d_start_queue(ep); 83990fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&u3d->lock, flags); 84090fccb52SAndrzej Pietrasiewicz return 0; 84190fccb52SAndrzej Pietrasiewicz } 84290fccb52SAndrzej Pietrasiewicz 84390fccb52SAndrzej Pietrasiewicz /* dequeues (cancels, unlinks) an I/O request from an endpoint */ 84490fccb52SAndrzej Pietrasiewicz static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) 84590fccb52SAndrzej Pietrasiewicz { 84690fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 847299dd6ebSJakob Koschel struct mv_u3d_req *req = NULL, *iter; 84890fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 84990fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep_context *ep_context; 85090fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *next_req; 85190fccb52SAndrzej Pietrasiewicz 85290fccb52SAndrzej Pietrasiewicz unsigned long flags; 85390fccb52SAndrzej Pietrasiewicz int ret = 0; 85490fccb52SAndrzej Pietrasiewicz 85590fccb52SAndrzej Pietrasiewicz if (!_ep || !_req) 85690fccb52SAndrzej Pietrasiewicz return -EINVAL; 85790fccb52SAndrzej Pietrasiewicz 85890fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct mv_u3d_ep, ep); 85990fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 86090fccb52SAndrzej Pietrasiewicz 86190fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->u3d->lock, flags); 86290fccb52SAndrzej Pietrasiewicz 86390fccb52SAndrzej Pietrasiewicz /* make sure it's actually queued on this endpoint */ 864299dd6ebSJakob Koschel list_for_each_entry(iter, &ep->queue, queue) { 865299dd6ebSJakob Koschel if (&iter->req != _req) 866299dd6ebSJakob Koschel continue; 867299dd6ebSJakob Koschel req = iter; 86890fccb52SAndrzej Pietrasiewicz break; 86990fccb52SAndrzej Pietrasiewicz } 870299dd6ebSJakob Koschel if (!req) { 87190fccb52SAndrzej Pietrasiewicz ret = -EINVAL; 87290fccb52SAndrzej Pietrasiewicz goto out; 87390fccb52SAndrzej Pietrasiewicz } 87490fccb52SAndrzej Pietrasiewicz 87590fccb52SAndrzej Pietrasiewicz /* The request is in progress, or completed but not dequeued */ 87690fccb52SAndrzej Pietrasiewicz if (ep->queue.next == &req->queue) { 87790fccb52SAndrzej Pietrasiewicz _req->status = -ECONNRESET; 87890fccb52SAndrzej Pietrasiewicz mv_u3d_ep_fifo_flush(_ep); 87990fccb52SAndrzej Pietrasiewicz 88090fccb52SAndrzej Pietrasiewicz /* The request isn't the last request in this ep queue */ 88190fccb52SAndrzej Pietrasiewicz if (req->queue.next != &ep->queue) { 88290fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, 88390fccb52SAndrzej Pietrasiewicz "it is the last request in this ep queue\n"); 88490fccb52SAndrzej Pietrasiewicz ep_context = ep->ep_context; 88590fccb52SAndrzej Pietrasiewicz next_req = list_entry(req->queue.next, 88690fccb52SAndrzej Pietrasiewicz struct mv_u3d_req, queue); 88790fccb52SAndrzej Pietrasiewicz 88890fccb52SAndrzej Pietrasiewicz /* Point first TRB of next request to the EP context. */ 88990fccb52SAndrzej Pietrasiewicz iowrite32((unsigned long) next_req->trb_head, 89090fccb52SAndrzej Pietrasiewicz &ep_context->trb_addr_lo); 89190fccb52SAndrzej Pietrasiewicz } else { 89290fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep_context *ep_context; 89390fccb52SAndrzej Pietrasiewicz ep_context = ep->ep_context; 89490fccb52SAndrzej Pietrasiewicz ep_context->trb_addr_lo = 0; 89590fccb52SAndrzej Pietrasiewicz ep_context->trb_addr_hi = 0; 89690fccb52SAndrzej Pietrasiewicz } 89790fccb52SAndrzej Pietrasiewicz 89890fccb52SAndrzej Pietrasiewicz } else 89990fccb52SAndrzej Pietrasiewicz WARN_ON(1); 90090fccb52SAndrzej Pietrasiewicz 90190fccb52SAndrzej Pietrasiewicz mv_u3d_done(ep, req, -ECONNRESET); 90290fccb52SAndrzej Pietrasiewicz 90390fccb52SAndrzej Pietrasiewicz /* remove the req from the ep req list */ 90490fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->req_list)) { 90590fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *curr_req; 90690fccb52SAndrzej Pietrasiewicz curr_req = list_entry(ep->req_list.next, 90790fccb52SAndrzej Pietrasiewicz struct mv_u3d_req, list); 90890fccb52SAndrzej Pietrasiewicz if (curr_req == req) { 90990fccb52SAndrzej Pietrasiewicz list_del_init(&req->list); 91090fccb52SAndrzej Pietrasiewicz ep->processing = 0; 91190fccb52SAndrzej Pietrasiewicz } 91290fccb52SAndrzej Pietrasiewicz } 91390fccb52SAndrzej Pietrasiewicz 91490fccb52SAndrzej Pietrasiewicz out: 91590fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->u3d->lock, flags); 91690fccb52SAndrzej Pietrasiewicz return ret; 91790fccb52SAndrzej Pietrasiewicz } 91890fccb52SAndrzej Pietrasiewicz 91990fccb52SAndrzej Pietrasiewicz static void 92090fccb52SAndrzej Pietrasiewicz mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall) 92190fccb52SAndrzej Pietrasiewicz { 92290fccb52SAndrzej Pietrasiewicz u32 tmp; 92390fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep = u3d->eps; 92490fccb52SAndrzej Pietrasiewicz 92590fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "%s\n", __func__); 92690fccb52SAndrzej Pietrasiewicz if (direction == MV_U3D_EP_DIR_OUT) { 92790fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 92890fccb52SAndrzej Pietrasiewicz if (stall) 92990fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_HALT; 93090fccb52SAndrzej Pietrasiewicz else 93190fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_EPXCR_EP_HALT; 93290fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); 93390fccb52SAndrzej Pietrasiewicz } else { 93490fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 93590fccb52SAndrzej Pietrasiewicz if (stall) 93690fccb52SAndrzej Pietrasiewicz tmp |= MV_U3D_EPXCR_EP_HALT; 93790fccb52SAndrzej Pietrasiewicz else 93890fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_EPXCR_EP_HALT; 93990fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); 94090fccb52SAndrzej Pietrasiewicz } 94190fccb52SAndrzej Pietrasiewicz } 94290fccb52SAndrzej Pietrasiewicz 94390fccb52SAndrzej Pietrasiewicz static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) 94490fccb52SAndrzej Pietrasiewicz { 94590fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 946c9c5f057SJohan Hovold unsigned long flags; 94790fccb52SAndrzej Pietrasiewicz int status = 0; 94890fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 94990fccb52SAndrzej Pietrasiewicz 95090fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct mv_u3d_ep, ep); 95190fccb52SAndrzej Pietrasiewicz u3d = ep->u3d; 95290fccb52SAndrzej Pietrasiewicz if (!ep->ep.desc) { 95390fccb52SAndrzej Pietrasiewicz status = -EINVAL; 95490fccb52SAndrzej Pietrasiewicz goto out; 95590fccb52SAndrzej Pietrasiewicz } 95690fccb52SAndrzej Pietrasiewicz 95790fccb52SAndrzej Pietrasiewicz if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { 95890fccb52SAndrzej Pietrasiewicz status = -EOPNOTSUPP; 95990fccb52SAndrzej Pietrasiewicz goto out; 96090fccb52SAndrzej Pietrasiewicz } 96190fccb52SAndrzej Pietrasiewicz 96290fccb52SAndrzej Pietrasiewicz /* 96390fccb52SAndrzej Pietrasiewicz * Attempt to halt IN ep will fail if any transfer requests 96490fccb52SAndrzej Pietrasiewicz * are still queue 96590fccb52SAndrzej Pietrasiewicz */ 96690fccb52SAndrzej Pietrasiewicz if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN) 96790fccb52SAndrzej Pietrasiewicz && !list_empty(&ep->queue)) { 96890fccb52SAndrzej Pietrasiewicz status = -EAGAIN; 96990fccb52SAndrzej Pietrasiewicz goto out; 97090fccb52SAndrzej Pietrasiewicz } 97190fccb52SAndrzej Pietrasiewicz 97290fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->u3d->lock, flags); 97390fccb52SAndrzej Pietrasiewicz mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt); 97490fccb52SAndrzej Pietrasiewicz if (halt && wedge) 97590fccb52SAndrzej Pietrasiewicz ep->wedge = 1; 97690fccb52SAndrzej Pietrasiewicz else if (!halt) 97790fccb52SAndrzej Pietrasiewicz ep->wedge = 0; 97890fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->u3d->lock, flags); 97990fccb52SAndrzej Pietrasiewicz 98090fccb52SAndrzej Pietrasiewicz if (ep->ep_num == 0) 98190fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = MV_U3D_EP_DIR_OUT; 98290fccb52SAndrzej Pietrasiewicz out: 98390fccb52SAndrzej Pietrasiewicz return status; 98490fccb52SAndrzej Pietrasiewicz } 98590fccb52SAndrzej Pietrasiewicz 98690fccb52SAndrzej Pietrasiewicz static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt) 98790fccb52SAndrzej Pietrasiewicz { 98890fccb52SAndrzej Pietrasiewicz return mv_u3d_ep_set_halt_wedge(_ep, halt, 0); 98990fccb52SAndrzej Pietrasiewicz } 99090fccb52SAndrzej Pietrasiewicz 99190fccb52SAndrzej Pietrasiewicz static int mv_u3d_ep_set_wedge(struct usb_ep *_ep) 99290fccb52SAndrzej Pietrasiewicz { 99390fccb52SAndrzej Pietrasiewicz return mv_u3d_ep_set_halt_wedge(_ep, 1, 1); 99490fccb52SAndrzej Pietrasiewicz } 99590fccb52SAndrzej Pietrasiewicz 996977ac789SBhumika Goyal static const struct usb_ep_ops mv_u3d_ep_ops = { 99790fccb52SAndrzej Pietrasiewicz .enable = mv_u3d_ep_enable, 99890fccb52SAndrzej Pietrasiewicz .disable = mv_u3d_ep_disable, 99990fccb52SAndrzej Pietrasiewicz 100090fccb52SAndrzej Pietrasiewicz .alloc_request = mv_u3d_alloc_request, 100190fccb52SAndrzej Pietrasiewicz .free_request = mv_u3d_free_request, 100290fccb52SAndrzej Pietrasiewicz 100390fccb52SAndrzej Pietrasiewicz .queue = mv_u3d_ep_queue, 100490fccb52SAndrzej Pietrasiewicz .dequeue = mv_u3d_ep_dequeue, 100590fccb52SAndrzej Pietrasiewicz 100690fccb52SAndrzej Pietrasiewicz .set_wedge = mv_u3d_ep_set_wedge, 100790fccb52SAndrzej Pietrasiewicz .set_halt = mv_u3d_ep_set_halt, 100890fccb52SAndrzej Pietrasiewicz .fifo_flush = mv_u3d_ep_fifo_flush, 100990fccb52SAndrzej Pietrasiewicz }; 101090fccb52SAndrzej Pietrasiewicz 101190fccb52SAndrzej Pietrasiewicz static void mv_u3d_controller_stop(struct mv_u3d *u3d) 101290fccb52SAndrzej Pietrasiewicz { 101390fccb52SAndrzej Pietrasiewicz u32 tmp; 101490fccb52SAndrzej Pietrasiewicz 101590fccb52SAndrzej Pietrasiewicz if (!u3d->clock_gating && u3d->vbus_valid_detect) 101690fccb52SAndrzej Pietrasiewicz iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID, 101790fccb52SAndrzej Pietrasiewicz &u3d->vuc_regs->intrenable); 101890fccb52SAndrzej Pietrasiewicz else 101990fccb52SAndrzej Pietrasiewicz iowrite32(0, &u3d->vuc_regs->intrenable); 102090fccb52SAndrzej Pietrasiewicz iowrite32(~0x0, &u3d->vuc_regs->endcomplete); 102190fccb52SAndrzej Pietrasiewicz iowrite32(~0x0, &u3d->vuc_regs->trbunderrun); 102290fccb52SAndrzej Pietrasiewicz iowrite32(~0x0, &u3d->vuc_regs->trbcomplete); 102390fccb52SAndrzej Pietrasiewicz iowrite32(~0x0, &u3d->vuc_regs->linkchange); 102490fccb52SAndrzej Pietrasiewicz iowrite32(0x1, &u3d->vuc_regs->setuplock); 102590fccb52SAndrzej Pietrasiewicz 102690fccb52SAndrzej Pietrasiewicz /* Reset the RUN bit in the command register to stop USB */ 102790fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->op_regs->usbcmd); 102890fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_CMD_RUN_STOP; 102990fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->op_regs->usbcmd); 103090fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n", 103190fccb52SAndrzej Pietrasiewicz ioread32(&u3d->op_regs->usbcmd)); 103290fccb52SAndrzej Pietrasiewicz } 103390fccb52SAndrzej Pietrasiewicz 103490fccb52SAndrzej Pietrasiewicz static void mv_u3d_controller_start(struct mv_u3d *u3d) 103590fccb52SAndrzej Pietrasiewicz { 103690fccb52SAndrzej Pietrasiewicz u32 usbintr; 103790fccb52SAndrzej Pietrasiewicz u32 temp; 103890fccb52SAndrzej Pietrasiewicz 103990fccb52SAndrzej Pietrasiewicz /* enable link LTSSM state machine */ 104090fccb52SAndrzej Pietrasiewicz temp = ioread32(&u3d->vuc_regs->ltssm); 104190fccb52SAndrzej Pietrasiewicz temp |= MV_U3D_LTSSM_PHY_INIT_DONE; 104290fccb52SAndrzej Pietrasiewicz iowrite32(temp, &u3d->vuc_regs->ltssm); 104390fccb52SAndrzej Pietrasiewicz 104490fccb52SAndrzej Pietrasiewicz /* Enable interrupts */ 104590fccb52SAndrzej Pietrasiewicz usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR | 104690fccb52SAndrzej Pietrasiewicz MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE | 104790fccb52SAndrzej Pietrasiewicz MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP | 104890fccb52SAndrzej Pietrasiewicz (u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0); 104990fccb52SAndrzej Pietrasiewicz iowrite32(usbintr, &u3d->vuc_regs->intrenable); 105090fccb52SAndrzej Pietrasiewicz 105190fccb52SAndrzej Pietrasiewicz /* Enable ctrl ep */ 105290fccb52SAndrzej Pietrasiewicz iowrite32(0x1, &u3d->vuc_regs->ctrlepenable); 105390fccb52SAndrzej Pietrasiewicz 105490fccb52SAndrzej Pietrasiewicz /* Set the Run bit in the command register */ 105590fccb52SAndrzej Pietrasiewicz iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd); 105690fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n", 105790fccb52SAndrzej Pietrasiewicz ioread32(&u3d->op_regs->usbcmd)); 105890fccb52SAndrzej Pietrasiewicz } 105990fccb52SAndrzej Pietrasiewicz 106090fccb52SAndrzej Pietrasiewicz static int mv_u3d_controller_reset(struct mv_u3d *u3d) 106190fccb52SAndrzej Pietrasiewicz { 106290fccb52SAndrzej Pietrasiewicz unsigned int loops; 106390fccb52SAndrzej Pietrasiewicz u32 tmp; 106490fccb52SAndrzej Pietrasiewicz 106590fccb52SAndrzej Pietrasiewicz /* Stop the controller */ 106690fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->op_regs->usbcmd); 106790fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_CMD_RUN_STOP; 106890fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->op_regs->usbcmd); 106990fccb52SAndrzej Pietrasiewicz 107090fccb52SAndrzej Pietrasiewicz /* Reset the controller to get default values */ 107190fccb52SAndrzej Pietrasiewicz iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd); 107290fccb52SAndrzej Pietrasiewicz 107390fccb52SAndrzej Pietrasiewicz /* wait for reset to complete */ 107490fccb52SAndrzej Pietrasiewicz loops = LOOPS(MV_U3D_RESET_TIMEOUT); 107590fccb52SAndrzej Pietrasiewicz while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) { 107690fccb52SAndrzej Pietrasiewicz if (loops == 0) { 107790fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 107890fccb52SAndrzej Pietrasiewicz "Wait for RESET completed TIMEOUT\n"); 107990fccb52SAndrzej Pietrasiewicz return -ETIMEDOUT; 108090fccb52SAndrzej Pietrasiewicz } 108190fccb52SAndrzej Pietrasiewicz loops--; 108290fccb52SAndrzej Pietrasiewicz udelay(LOOPS_USEC); 108390fccb52SAndrzej Pietrasiewicz } 108490fccb52SAndrzej Pietrasiewicz 108590fccb52SAndrzej Pietrasiewicz /* Configure the Endpoint Context Address */ 108690fccb52SAndrzej Pietrasiewicz iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl); 108790fccb52SAndrzej Pietrasiewicz iowrite32(0, &u3d->op_regs->dcbaaph); 108890fccb52SAndrzej Pietrasiewicz 108990fccb52SAndrzej Pietrasiewicz return 0; 109090fccb52SAndrzej Pietrasiewicz } 109190fccb52SAndrzej Pietrasiewicz 109290fccb52SAndrzej Pietrasiewicz static int mv_u3d_enable(struct mv_u3d *u3d) 109390fccb52SAndrzej Pietrasiewicz { 109490fccb52SAndrzej Pietrasiewicz struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); 109590fccb52SAndrzej Pietrasiewicz int retval; 109690fccb52SAndrzej Pietrasiewicz 109790fccb52SAndrzej Pietrasiewicz if (u3d->active) 109890fccb52SAndrzej Pietrasiewicz return 0; 109990fccb52SAndrzej Pietrasiewicz 110090fccb52SAndrzej Pietrasiewicz if (!u3d->clock_gating) { 110190fccb52SAndrzej Pietrasiewicz u3d->active = 1; 110290fccb52SAndrzej Pietrasiewicz return 0; 110390fccb52SAndrzej Pietrasiewicz } 110490fccb52SAndrzej Pietrasiewicz 110590fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "enable u3d\n"); 110690fccb52SAndrzej Pietrasiewicz clk_enable(u3d->clk); 110790fccb52SAndrzej Pietrasiewicz if (pdata->phy_init) { 110890fccb52SAndrzej Pietrasiewicz retval = pdata->phy_init(u3d->phy_regs); 110990fccb52SAndrzej Pietrasiewicz if (retval) { 111090fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 111190fccb52SAndrzej Pietrasiewicz "init phy error %d\n", retval); 111290fccb52SAndrzej Pietrasiewicz clk_disable(u3d->clk); 111390fccb52SAndrzej Pietrasiewicz return retval; 111490fccb52SAndrzej Pietrasiewicz } 111590fccb52SAndrzej Pietrasiewicz } 111690fccb52SAndrzej Pietrasiewicz u3d->active = 1; 111790fccb52SAndrzej Pietrasiewicz 111890fccb52SAndrzej Pietrasiewicz return 0; 111990fccb52SAndrzej Pietrasiewicz } 112090fccb52SAndrzej Pietrasiewicz 112190fccb52SAndrzej Pietrasiewicz static void mv_u3d_disable(struct mv_u3d *u3d) 112290fccb52SAndrzej Pietrasiewicz { 112390fccb52SAndrzej Pietrasiewicz struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); 112490fccb52SAndrzej Pietrasiewicz if (u3d->clock_gating && u3d->active) { 112590fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "disable u3d\n"); 112690fccb52SAndrzej Pietrasiewicz if (pdata->phy_deinit) 112790fccb52SAndrzej Pietrasiewicz pdata->phy_deinit(u3d->phy_regs); 112890fccb52SAndrzej Pietrasiewicz clk_disable(u3d->clk); 112990fccb52SAndrzej Pietrasiewicz u3d->active = 0; 113090fccb52SAndrzej Pietrasiewicz } 113190fccb52SAndrzej Pietrasiewicz } 113290fccb52SAndrzej Pietrasiewicz 113390fccb52SAndrzej Pietrasiewicz static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active) 113490fccb52SAndrzej Pietrasiewicz { 113590fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d; 113690fccb52SAndrzej Pietrasiewicz unsigned long flags; 113790fccb52SAndrzej Pietrasiewicz int retval = 0; 113890fccb52SAndrzej Pietrasiewicz 113990fccb52SAndrzej Pietrasiewicz u3d = container_of(gadget, struct mv_u3d, gadget); 114090fccb52SAndrzej Pietrasiewicz 114190fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&u3d->lock, flags); 114290fccb52SAndrzej Pietrasiewicz 114390fccb52SAndrzej Pietrasiewicz u3d->vbus_active = (is_active != 0); 114490fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", 114590fccb52SAndrzej Pietrasiewicz __func__, u3d->softconnect, u3d->vbus_active); 114690fccb52SAndrzej Pietrasiewicz /* 114790fccb52SAndrzej Pietrasiewicz * 1. external VBUS detect: we can disable/enable clock on demand. 114890fccb52SAndrzej Pietrasiewicz * 2. UDC VBUS detect: we have to enable clock all the time. 114990fccb52SAndrzej Pietrasiewicz * 3. No VBUS detect: we have to enable clock all the time. 115090fccb52SAndrzej Pietrasiewicz */ 115190fccb52SAndrzej Pietrasiewicz if (u3d->driver && u3d->softconnect && u3d->vbus_active) { 115290fccb52SAndrzej Pietrasiewicz retval = mv_u3d_enable(u3d); 115390fccb52SAndrzej Pietrasiewicz if (retval == 0) { 115490fccb52SAndrzej Pietrasiewicz /* 115590fccb52SAndrzej Pietrasiewicz * after clock is disabled, we lost all the register 115690fccb52SAndrzej Pietrasiewicz * context. We have to re-init registers 115790fccb52SAndrzej Pietrasiewicz */ 115890fccb52SAndrzej Pietrasiewicz mv_u3d_controller_reset(u3d); 115990fccb52SAndrzej Pietrasiewicz mv_u3d_ep0_reset(u3d); 116090fccb52SAndrzej Pietrasiewicz mv_u3d_controller_start(u3d); 116190fccb52SAndrzej Pietrasiewicz } 116290fccb52SAndrzej Pietrasiewicz } else if (u3d->driver && u3d->softconnect) { 116390fccb52SAndrzej Pietrasiewicz if (!u3d->active) 116490fccb52SAndrzej Pietrasiewicz goto out; 116590fccb52SAndrzej Pietrasiewicz 116690fccb52SAndrzej Pietrasiewicz /* stop all the transfer in queue*/ 116790fccb52SAndrzej Pietrasiewicz mv_u3d_stop_activity(u3d, u3d->driver); 116890fccb52SAndrzej Pietrasiewicz mv_u3d_controller_stop(u3d); 116990fccb52SAndrzej Pietrasiewicz mv_u3d_disable(u3d); 117090fccb52SAndrzej Pietrasiewicz } 117190fccb52SAndrzej Pietrasiewicz 117290fccb52SAndrzej Pietrasiewicz out: 117390fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&u3d->lock, flags); 117490fccb52SAndrzej Pietrasiewicz return retval; 117590fccb52SAndrzej Pietrasiewicz } 117690fccb52SAndrzej Pietrasiewicz 117790fccb52SAndrzej Pietrasiewicz /* constrain controller's VBUS power usage 117890fccb52SAndrzej Pietrasiewicz * This call is used by gadget drivers during SET_CONFIGURATION calls, 117990fccb52SAndrzej Pietrasiewicz * reporting how much power the device may consume. For example, this 118090fccb52SAndrzej Pietrasiewicz * could affect how quickly batteries are recharged. 118190fccb52SAndrzej Pietrasiewicz * 118290fccb52SAndrzej Pietrasiewicz * Returns zero on success, else negative errno. 118390fccb52SAndrzej Pietrasiewicz */ 118490fccb52SAndrzej Pietrasiewicz static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA) 118590fccb52SAndrzej Pietrasiewicz { 118690fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); 118790fccb52SAndrzej Pietrasiewicz 118890fccb52SAndrzej Pietrasiewicz u3d->power = mA; 118990fccb52SAndrzej Pietrasiewicz 119090fccb52SAndrzej Pietrasiewicz return 0; 119190fccb52SAndrzej Pietrasiewicz } 119290fccb52SAndrzej Pietrasiewicz 119390fccb52SAndrzej Pietrasiewicz static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on) 119490fccb52SAndrzej Pietrasiewicz { 119590fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); 119690fccb52SAndrzej Pietrasiewicz unsigned long flags; 119790fccb52SAndrzej Pietrasiewicz int retval = 0; 119890fccb52SAndrzej Pietrasiewicz 119990fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&u3d->lock, flags); 120090fccb52SAndrzej Pietrasiewicz 120190fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", 120290fccb52SAndrzej Pietrasiewicz __func__, u3d->softconnect, u3d->vbus_active); 120390fccb52SAndrzej Pietrasiewicz u3d->softconnect = (is_on != 0); 120490fccb52SAndrzej Pietrasiewicz if (u3d->driver && u3d->softconnect && u3d->vbus_active) { 120590fccb52SAndrzej Pietrasiewicz retval = mv_u3d_enable(u3d); 120690fccb52SAndrzej Pietrasiewicz if (retval == 0) { 120790fccb52SAndrzej Pietrasiewicz /* 120890fccb52SAndrzej Pietrasiewicz * after clock is disabled, we lost all the register 120990fccb52SAndrzej Pietrasiewicz * context. We have to re-init registers 121090fccb52SAndrzej Pietrasiewicz */ 121190fccb52SAndrzej Pietrasiewicz mv_u3d_controller_reset(u3d); 121290fccb52SAndrzej Pietrasiewicz mv_u3d_ep0_reset(u3d); 121390fccb52SAndrzej Pietrasiewicz mv_u3d_controller_start(u3d); 121490fccb52SAndrzej Pietrasiewicz } 121590fccb52SAndrzej Pietrasiewicz } else if (u3d->driver && u3d->vbus_active) { 121690fccb52SAndrzej Pietrasiewicz /* stop all the transfer in queue*/ 121790fccb52SAndrzej Pietrasiewicz mv_u3d_stop_activity(u3d, u3d->driver); 121890fccb52SAndrzej Pietrasiewicz mv_u3d_controller_stop(u3d); 121990fccb52SAndrzej Pietrasiewicz mv_u3d_disable(u3d); 122090fccb52SAndrzej Pietrasiewicz } 122190fccb52SAndrzej Pietrasiewicz 122290fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&u3d->lock, flags); 122390fccb52SAndrzej Pietrasiewicz 122490fccb52SAndrzej Pietrasiewicz return retval; 122590fccb52SAndrzej Pietrasiewicz } 122690fccb52SAndrzej Pietrasiewicz 122790fccb52SAndrzej Pietrasiewicz static int mv_u3d_start(struct usb_gadget *g, 122890fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver) 122990fccb52SAndrzej Pietrasiewicz { 123090fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); 123190fccb52SAndrzej Pietrasiewicz struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); 123290fccb52SAndrzej Pietrasiewicz unsigned long flags; 123390fccb52SAndrzej Pietrasiewicz 123490fccb52SAndrzej Pietrasiewicz if (u3d->driver) 123590fccb52SAndrzej Pietrasiewicz return -EBUSY; 123690fccb52SAndrzej Pietrasiewicz 123790fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&u3d->lock, flags); 123890fccb52SAndrzej Pietrasiewicz 123990fccb52SAndrzej Pietrasiewicz if (!u3d->clock_gating) { 124090fccb52SAndrzej Pietrasiewicz clk_enable(u3d->clk); 124190fccb52SAndrzej Pietrasiewicz if (pdata->phy_init) 124290fccb52SAndrzej Pietrasiewicz pdata->phy_init(u3d->phy_regs); 124390fccb52SAndrzej Pietrasiewicz } 124490fccb52SAndrzej Pietrasiewicz 124590fccb52SAndrzej Pietrasiewicz /* hook up the driver ... */ 124690fccb52SAndrzej Pietrasiewicz u3d->driver = driver; 124790fccb52SAndrzej Pietrasiewicz 124890fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = USB_DIR_OUT; 124990fccb52SAndrzej Pietrasiewicz 125090fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&u3d->lock, flags); 125190fccb52SAndrzej Pietrasiewicz 125290fccb52SAndrzej Pietrasiewicz u3d->vbus_valid_detect = 1; 125390fccb52SAndrzej Pietrasiewicz 125490fccb52SAndrzej Pietrasiewicz return 0; 125590fccb52SAndrzej Pietrasiewicz } 125690fccb52SAndrzej Pietrasiewicz 125722835b80SFelipe Balbi static int mv_u3d_stop(struct usb_gadget *g) 125890fccb52SAndrzej Pietrasiewicz { 125990fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); 126090fccb52SAndrzej Pietrasiewicz struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); 126190fccb52SAndrzej Pietrasiewicz unsigned long flags; 126290fccb52SAndrzej Pietrasiewicz 126390fccb52SAndrzej Pietrasiewicz u3d->vbus_valid_detect = 0; 126490fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&u3d->lock, flags); 126590fccb52SAndrzej Pietrasiewicz 126690fccb52SAndrzej Pietrasiewicz /* enable clock to access controller register */ 126790fccb52SAndrzej Pietrasiewicz clk_enable(u3d->clk); 126890fccb52SAndrzej Pietrasiewicz if (pdata->phy_init) 126990fccb52SAndrzej Pietrasiewicz pdata->phy_init(u3d->phy_regs); 127090fccb52SAndrzej Pietrasiewicz 127190fccb52SAndrzej Pietrasiewicz mv_u3d_controller_stop(u3d); 127290fccb52SAndrzej Pietrasiewicz /* stop all usb activities */ 127390fccb52SAndrzej Pietrasiewicz u3d->gadget.speed = USB_SPEED_UNKNOWN; 1274a19f3787SFelipe Balbi mv_u3d_stop_activity(u3d, NULL); 127590fccb52SAndrzej Pietrasiewicz mv_u3d_disable(u3d); 127690fccb52SAndrzej Pietrasiewicz 127790fccb52SAndrzej Pietrasiewicz if (pdata->phy_deinit) 127890fccb52SAndrzej Pietrasiewicz pdata->phy_deinit(u3d->phy_regs); 127990fccb52SAndrzej Pietrasiewicz clk_disable(u3d->clk); 128090fccb52SAndrzej Pietrasiewicz 128190fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&u3d->lock, flags); 128290fccb52SAndrzej Pietrasiewicz 128390fccb52SAndrzej Pietrasiewicz u3d->driver = NULL; 128490fccb52SAndrzej Pietrasiewicz 128590fccb52SAndrzej Pietrasiewicz return 0; 128690fccb52SAndrzej Pietrasiewicz } 128790fccb52SAndrzej Pietrasiewicz 128890fccb52SAndrzej Pietrasiewicz /* device controller usb_gadget_ops structure */ 128990fccb52SAndrzej Pietrasiewicz static const struct usb_gadget_ops mv_u3d_ops = { 129090fccb52SAndrzej Pietrasiewicz /* notify controller that VBUS is powered or not */ 129190fccb52SAndrzej Pietrasiewicz .vbus_session = mv_u3d_vbus_session, 129290fccb52SAndrzej Pietrasiewicz 129390fccb52SAndrzej Pietrasiewicz /* constrain controller's VBUS power usage */ 129490fccb52SAndrzej Pietrasiewicz .vbus_draw = mv_u3d_vbus_draw, 129590fccb52SAndrzej Pietrasiewicz 129690fccb52SAndrzej Pietrasiewicz .pullup = mv_u3d_pullup, 129790fccb52SAndrzej Pietrasiewicz .udc_start = mv_u3d_start, 129890fccb52SAndrzej Pietrasiewicz .udc_stop = mv_u3d_stop, 129990fccb52SAndrzej Pietrasiewicz }; 130090fccb52SAndrzej Pietrasiewicz 130190fccb52SAndrzej Pietrasiewicz static int mv_u3d_eps_init(struct mv_u3d *u3d) 130290fccb52SAndrzej Pietrasiewicz { 130390fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 130490fccb52SAndrzej Pietrasiewicz char name[14]; 130590fccb52SAndrzej Pietrasiewicz int i; 130690fccb52SAndrzej Pietrasiewicz 130790fccb52SAndrzej Pietrasiewicz /* initialize ep0, ep0 in/out use eps[1] */ 130890fccb52SAndrzej Pietrasiewicz ep = &u3d->eps[1]; 130990fccb52SAndrzej Pietrasiewicz ep->u3d = u3d; 131090fccb52SAndrzej Pietrasiewicz strncpy(ep->name, "ep0", sizeof(ep->name)); 131190fccb52SAndrzej Pietrasiewicz ep->ep.name = ep->name; 131290fccb52SAndrzej Pietrasiewicz ep->ep.ops = &mv_u3d_ep_ops; 131390fccb52SAndrzej Pietrasiewicz ep->wedge = 0; 131490fccb52SAndrzej Pietrasiewicz usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE); 1315c12a3062SRobert Baldyga ep->ep.caps.type_control = true; 1316c12a3062SRobert Baldyga ep->ep.caps.dir_in = true; 1317c12a3062SRobert Baldyga ep->ep.caps.dir_out = true; 131890fccb52SAndrzej Pietrasiewicz ep->ep_num = 0; 131990fccb52SAndrzej Pietrasiewicz ep->ep.desc = &mv_u3d_ep0_desc; 132090fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->queue); 132190fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->req_list); 132290fccb52SAndrzej Pietrasiewicz ep->ep_type = USB_ENDPOINT_XFER_CONTROL; 132390fccb52SAndrzej Pietrasiewicz 132490fccb52SAndrzej Pietrasiewicz /* add ep0 ep_context */ 132590fccb52SAndrzej Pietrasiewicz ep->ep_context = &u3d->ep_context[1]; 132690fccb52SAndrzej Pietrasiewicz 132790fccb52SAndrzej Pietrasiewicz /* initialize other endpoints */ 132890fccb52SAndrzej Pietrasiewicz for (i = 2; i < u3d->max_eps * 2; i++) { 132990fccb52SAndrzej Pietrasiewicz ep = &u3d->eps[i]; 133090fccb52SAndrzej Pietrasiewicz if (i & 1) { 133190fccb52SAndrzej Pietrasiewicz snprintf(name, sizeof(name), "ep%din", i >> 1); 133290fccb52SAndrzej Pietrasiewicz ep->direction = MV_U3D_EP_DIR_IN; 1333c12a3062SRobert Baldyga ep->ep.caps.dir_in = true; 133490fccb52SAndrzej Pietrasiewicz } else { 133590fccb52SAndrzej Pietrasiewicz snprintf(name, sizeof(name), "ep%dout", i >> 1); 133690fccb52SAndrzej Pietrasiewicz ep->direction = MV_U3D_EP_DIR_OUT; 1337c12a3062SRobert Baldyga ep->ep.caps.dir_out = true; 133890fccb52SAndrzej Pietrasiewicz } 133990fccb52SAndrzej Pietrasiewicz ep->u3d = u3d; 134090fccb52SAndrzej Pietrasiewicz strncpy(ep->name, name, sizeof(ep->name)); 134190fccb52SAndrzej Pietrasiewicz ep->ep.name = ep->name; 134290fccb52SAndrzej Pietrasiewicz 1343c12a3062SRobert Baldyga ep->ep.caps.type_iso = true; 1344c12a3062SRobert Baldyga ep->ep.caps.type_bulk = true; 1345c12a3062SRobert Baldyga ep->ep.caps.type_int = true; 1346c12a3062SRobert Baldyga 134790fccb52SAndrzej Pietrasiewicz ep->ep.ops = &mv_u3d_ep_ops; 134890fccb52SAndrzej Pietrasiewicz usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); 134990fccb52SAndrzej Pietrasiewicz ep->ep_num = i / 2; 135090fccb52SAndrzej Pietrasiewicz 135190fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->queue); 135290fccb52SAndrzej Pietrasiewicz list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list); 135390fccb52SAndrzej Pietrasiewicz 135490fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->req_list); 135590fccb52SAndrzej Pietrasiewicz spin_lock_init(&ep->req_lock); 135690fccb52SAndrzej Pietrasiewicz ep->ep_context = &u3d->ep_context[i]; 135790fccb52SAndrzej Pietrasiewicz } 135890fccb52SAndrzej Pietrasiewicz 135990fccb52SAndrzej Pietrasiewicz return 0; 136090fccb52SAndrzej Pietrasiewicz } 136190fccb52SAndrzej Pietrasiewicz 136290fccb52SAndrzej Pietrasiewicz /* delete all endpoint requests, called with spinlock held */ 136390fccb52SAndrzej Pietrasiewicz static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status) 136490fccb52SAndrzej Pietrasiewicz { 136590fccb52SAndrzej Pietrasiewicz /* endpoint fifo flush */ 136690fccb52SAndrzej Pietrasiewicz mv_u3d_ep_fifo_flush(&ep->ep); 136790fccb52SAndrzej Pietrasiewicz 136890fccb52SAndrzej Pietrasiewicz while (!list_empty(&ep->queue)) { 136990fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *req = NULL; 137090fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct mv_u3d_req, queue); 137190fccb52SAndrzej Pietrasiewicz mv_u3d_done(ep, req, status); 137290fccb52SAndrzej Pietrasiewicz } 137390fccb52SAndrzej Pietrasiewicz } 137490fccb52SAndrzej Pietrasiewicz 137590fccb52SAndrzej Pietrasiewicz /* stop all USB activities */ 137690fccb52SAndrzej Pietrasiewicz static 137790fccb52SAndrzej Pietrasiewicz void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver) 137890fccb52SAndrzej Pietrasiewicz { 137990fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *ep; 138090fccb52SAndrzej Pietrasiewicz 138190fccb52SAndrzej Pietrasiewicz mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN); 138290fccb52SAndrzej Pietrasiewicz 138390fccb52SAndrzej Pietrasiewicz list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) { 138490fccb52SAndrzej Pietrasiewicz mv_u3d_nuke(ep, -ESHUTDOWN); 138590fccb52SAndrzej Pietrasiewicz } 138690fccb52SAndrzej Pietrasiewicz 138790fccb52SAndrzej Pietrasiewicz /* report disconnect; the driver is already quiesced */ 138890fccb52SAndrzej Pietrasiewicz if (driver) { 138990fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 139090fccb52SAndrzej Pietrasiewicz driver->disconnect(&u3d->gadget); 139190fccb52SAndrzej Pietrasiewicz spin_lock(&u3d->lock); 139290fccb52SAndrzej Pietrasiewicz } 139390fccb52SAndrzej Pietrasiewicz } 139490fccb52SAndrzej Pietrasiewicz 139590fccb52SAndrzej Pietrasiewicz static void mv_u3d_irq_process_error(struct mv_u3d *u3d) 139690fccb52SAndrzej Pietrasiewicz { 139790fccb52SAndrzej Pietrasiewicz /* Increment the error count */ 139890fccb52SAndrzej Pietrasiewicz u3d->errors++; 139990fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "%s\n", __func__); 140090fccb52SAndrzej Pietrasiewicz } 140190fccb52SAndrzej Pietrasiewicz 140290fccb52SAndrzej Pietrasiewicz static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d) 140390fccb52SAndrzej Pietrasiewicz { 140490fccb52SAndrzej Pietrasiewicz u32 linkchange; 140590fccb52SAndrzej Pietrasiewicz 140690fccb52SAndrzej Pietrasiewicz linkchange = ioread32(&u3d->vuc_regs->linkchange); 140790fccb52SAndrzej Pietrasiewicz iowrite32(linkchange, &u3d->vuc_regs->linkchange); 140890fccb52SAndrzej Pietrasiewicz 140990fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange); 141090fccb52SAndrzej Pietrasiewicz 141190fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) { 141290fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n", 141390fccb52SAndrzej Pietrasiewicz ioread32(&u3d->vuc_regs->ltssmstate)); 141490fccb52SAndrzej Pietrasiewicz 141590fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_DEFAULT; 141690fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = MV_U3D_EP_DIR_OUT; 141790fccb52SAndrzej Pietrasiewicz u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; 141890fccb52SAndrzej Pietrasiewicz 141990fccb52SAndrzej Pietrasiewicz /* set speed */ 142090fccb52SAndrzej Pietrasiewicz u3d->gadget.speed = USB_SPEED_SUPER; 142190fccb52SAndrzej Pietrasiewicz } 142290fccb52SAndrzej Pietrasiewicz 142390fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) { 142490fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "link suspend\n"); 142590fccb52SAndrzej Pietrasiewicz u3d->resume_state = u3d->usb_state; 142690fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_SUSPENDED; 142790fccb52SAndrzej Pietrasiewicz } 142890fccb52SAndrzej Pietrasiewicz 142990fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_RESUME) { 143090fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "link resume\n"); 143190fccb52SAndrzej Pietrasiewicz u3d->usb_state = u3d->resume_state; 143290fccb52SAndrzej Pietrasiewicz u3d->resume_state = 0; 143390fccb52SAndrzej Pietrasiewicz } 143490fccb52SAndrzej Pietrasiewicz 143590fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_WRESET) { 143690fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "warm reset\n"); 143790fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_POWERED; 143890fccb52SAndrzej Pietrasiewicz } 143990fccb52SAndrzej Pietrasiewicz 144090fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_HRESET) { 144190fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "hot reset\n"); 144290fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_DEFAULT; 144390fccb52SAndrzej Pietrasiewicz } 144490fccb52SAndrzej Pietrasiewicz 144590fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_INACT) 144690fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "inactive\n"); 144790fccb52SAndrzej Pietrasiewicz 144890fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0) 144990fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "ss.disabled\n"); 145090fccb52SAndrzej Pietrasiewicz 145190fccb52SAndrzej Pietrasiewicz if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) { 145290fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "vbus invalid\n"); 145390fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_ATTACHED; 145490fccb52SAndrzej Pietrasiewicz u3d->vbus_valid_detect = 1; 145590fccb52SAndrzej Pietrasiewicz /* if external vbus detect is not supported, 145690fccb52SAndrzej Pietrasiewicz * we handle it here. 145790fccb52SAndrzej Pietrasiewicz */ 145890fccb52SAndrzej Pietrasiewicz if (!u3d->vbus) { 145990fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 146090fccb52SAndrzej Pietrasiewicz mv_u3d_vbus_session(&u3d->gadget, 0); 146190fccb52SAndrzej Pietrasiewicz spin_lock(&u3d->lock); 146290fccb52SAndrzej Pietrasiewicz } 146390fccb52SAndrzej Pietrasiewicz } 146490fccb52SAndrzej Pietrasiewicz } 146590fccb52SAndrzej Pietrasiewicz 146690fccb52SAndrzej Pietrasiewicz static void mv_u3d_ch9setaddress(struct mv_u3d *u3d, 146790fccb52SAndrzej Pietrasiewicz struct usb_ctrlrequest *setup) 146890fccb52SAndrzej Pietrasiewicz { 146990fccb52SAndrzej Pietrasiewicz u32 tmp; 147090fccb52SAndrzej Pietrasiewicz 147190fccb52SAndrzej Pietrasiewicz if (u3d->usb_state != USB_STATE_DEFAULT) { 147290fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 147390fccb52SAndrzej Pietrasiewicz "%s, cannot setaddr in this state (%d)\n", 147490fccb52SAndrzej Pietrasiewicz __func__, u3d->usb_state); 147590fccb52SAndrzej Pietrasiewicz goto err; 147690fccb52SAndrzej Pietrasiewicz } 147790fccb52SAndrzej Pietrasiewicz 147890fccb52SAndrzej Pietrasiewicz u3d->dev_addr = (u8)setup->wValue; 147990fccb52SAndrzej Pietrasiewicz 148090fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr); 148190fccb52SAndrzej Pietrasiewicz 148290fccb52SAndrzej Pietrasiewicz if (u3d->dev_addr > 127) { 148390fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, 148490fccb52SAndrzej Pietrasiewicz "%s, u3d address is wrong (out of range)\n", __func__); 148590fccb52SAndrzej Pietrasiewicz u3d->dev_addr = 0; 148690fccb52SAndrzej Pietrasiewicz goto err; 148790fccb52SAndrzej Pietrasiewicz } 148890fccb52SAndrzej Pietrasiewicz 148990fccb52SAndrzej Pietrasiewicz /* update usb state */ 149090fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_ADDRESS; 149190fccb52SAndrzej Pietrasiewicz 149290fccb52SAndrzej Pietrasiewicz /* set the new address */ 149390fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr); 149490fccb52SAndrzej Pietrasiewicz tmp &= ~0x7F; 149590fccb52SAndrzej Pietrasiewicz tmp |= (u32)u3d->dev_addr; 149690fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr); 149790fccb52SAndrzej Pietrasiewicz 149890fccb52SAndrzej Pietrasiewicz return; 149990fccb52SAndrzej Pietrasiewicz err: 150090fccb52SAndrzej Pietrasiewicz mv_u3d_ep0_stall(u3d); 150190fccb52SAndrzej Pietrasiewicz } 150290fccb52SAndrzej Pietrasiewicz 150390fccb52SAndrzej Pietrasiewicz static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup) 150490fccb52SAndrzej Pietrasiewicz { 150590fccb52SAndrzej Pietrasiewicz if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 150690fccb52SAndrzej Pietrasiewicz if (setup->bRequest == USB_REQ_SET_CONFIGURATION) 150790fccb52SAndrzej Pietrasiewicz return 1; 150890fccb52SAndrzej Pietrasiewicz 150990fccb52SAndrzej Pietrasiewicz return 0; 151090fccb52SAndrzej Pietrasiewicz } 151190fccb52SAndrzej Pietrasiewicz 151290fccb52SAndrzej Pietrasiewicz static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num, 151390fccb52SAndrzej Pietrasiewicz struct usb_ctrlrequest *setup) 151490fccb52SAndrzej Pietrasiewicz __releases(&u3c->lock) 151590fccb52SAndrzej Pietrasiewicz __acquires(&u3c->lock) 151690fccb52SAndrzej Pietrasiewicz { 151790fccb52SAndrzej Pietrasiewicz bool delegate = false; 151890fccb52SAndrzej Pietrasiewicz 151990fccb52SAndrzej Pietrasiewicz mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN); 152090fccb52SAndrzej Pietrasiewicz 152190fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", 152290fccb52SAndrzej Pietrasiewicz setup->bRequestType, setup->bRequest, 152390fccb52SAndrzej Pietrasiewicz setup->wValue, setup->wIndex, setup->wLength); 152490fccb52SAndrzej Pietrasiewicz 152590fccb52SAndrzej Pietrasiewicz /* We process some stardard setup requests here */ 152690fccb52SAndrzej Pietrasiewicz if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { 152790fccb52SAndrzej Pietrasiewicz switch (setup->bRequest) { 152890fccb52SAndrzej Pietrasiewicz case USB_REQ_GET_STATUS: 152990fccb52SAndrzej Pietrasiewicz delegate = true; 153090fccb52SAndrzej Pietrasiewicz break; 153190fccb52SAndrzej Pietrasiewicz 153290fccb52SAndrzej Pietrasiewicz case USB_REQ_SET_ADDRESS: 153390fccb52SAndrzej Pietrasiewicz mv_u3d_ch9setaddress(u3d, setup); 153490fccb52SAndrzej Pietrasiewicz break; 153590fccb52SAndrzej Pietrasiewicz 153690fccb52SAndrzej Pietrasiewicz case USB_REQ_CLEAR_FEATURE: 153790fccb52SAndrzej Pietrasiewicz delegate = true; 153890fccb52SAndrzej Pietrasiewicz break; 153990fccb52SAndrzej Pietrasiewicz 154090fccb52SAndrzej Pietrasiewicz case USB_REQ_SET_FEATURE: 154190fccb52SAndrzej Pietrasiewicz delegate = true; 154290fccb52SAndrzej Pietrasiewicz break; 154390fccb52SAndrzej Pietrasiewicz 154490fccb52SAndrzej Pietrasiewicz default: 154590fccb52SAndrzej Pietrasiewicz delegate = true; 154690fccb52SAndrzej Pietrasiewicz } 154790fccb52SAndrzej Pietrasiewicz } else 154890fccb52SAndrzej Pietrasiewicz delegate = true; 154990fccb52SAndrzej Pietrasiewicz 155090fccb52SAndrzej Pietrasiewicz /* delegate USB standard requests to the gadget driver */ 155130755dd5SJason Yan if (delegate) { 155290fccb52SAndrzej Pietrasiewicz /* USB requests handled by gadget */ 155390fccb52SAndrzej Pietrasiewicz if (setup->wLength) { 155490fccb52SAndrzej Pietrasiewicz /* DATA phase from gadget, STATUS phase from u3d */ 155590fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN) 155690fccb52SAndrzej Pietrasiewicz ? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT; 155790fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 155890fccb52SAndrzej Pietrasiewicz if (u3d->driver->setup(&u3d->gadget, 155990fccb52SAndrzej Pietrasiewicz &u3d->local_setup_buff) < 0) { 156090fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "setup error!\n"); 156190fccb52SAndrzej Pietrasiewicz mv_u3d_ep0_stall(u3d); 156290fccb52SAndrzej Pietrasiewicz } 156390fccb52SAndrzej Pietrasiewicz spin_lock(&u3d->lock); 156490fccb52SAndrzej Pietrasiewicz } else { 156590fccb52SAndrzej Pietrasiewicz /* no DATA phase, STATUS phase from gadget */ 156690fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = MV_U3D_EP_DIR_IN; 156790fccb52SAndrzej Pietrasiewicz u3d->ep0_state = MV_U3D_STATUS_STAGE; 156890fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 156990fccb52SAndrzej Pietrasiewicz if (u3d->driver->setup(&u3d->gadget, 157090fccb52SAndrzej Pietrasiewicz &u3d->local_setup_buff) < 0) 157190fccb52SAndrzej Pietrasiewicz mv_u3d_ep0_stall(u3d); 157290fccb52SAndrzej Pietrasiewicz spin_lock(&u3d->lock); 157390fccb52SAndrzej Pietrasiewicz } 157490fccb52SAndrzej Pietrasiewicz 157590fccb52SAndrzej Pietrasiewicz if (mv_u3d_is_set_configuration(setup)) { 157690fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "u3d configured\n"); 157790fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_CONFIGURED; 157890fccb52SAndrzej Pietrasiewicz } 157990fccb52SAndrzej Pietrasiewicz } 158090fccb52SAndrzej Pietrasiewicz } 158190fccb52SAndrzej Pietrasiewicz 158290fccb52SAndrzej Pietrasiewicz static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr) 158390fccb52SAndrzej Pietrasiewicz { 158490fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep_context *epcontext; 158590fccb52SAndrzej Pietrasiewicz 158690fccb52SAndrzej Pietrasiewicz epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN]; 158790fccb52SAndrzej Pietrasiewicz 158890fccb52SAndrzej Pietrasiewicz /* Copy the setup packet to local buffer */ 158990fccb52SAndrzej Pietrasiewicz memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8); 159090fccb52SAndrzej Pietrasiewicz } 159190fccb52SAndrzej Pietrasiewicz 159290fccb52SAndrzej Pietrasiewicz static void mv_u3d_irq_process_setup(struct mv_u3d *u3d) 159390fccb52SAndrzej Pietrasiewicz { 159490fccb52SAndrzej Pietrasiewicz u32 tmp, i; 159590fccb52SAndrzej Pietrasiewicz /* Process all Setup packet received interrupts */ 159690fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->setuplock); 159790fccb52SAndrzej Pietrasiewicz if (tmp) { 159890fccb52SAndrzej Pietrasiewicz for (i = 0; i < u3d->max_eps; i++) { 159990fccb52SAndrzej Pietrasiewicz if (tmp & (1 << i)) { 160090fccb52SAndrzej Pietrasiewicz mv_u3d_get_setup_data(u3d, i, 160190fccb52SAndrzej Pietrasiewicz (u8 *)(&u3d->local_setup_buff)); 160290fccb52SAndrzej Pietrasiewicz mv_u3d_handle_setup_packet(u3d, i, 160390fccb52SAndrzej Pietrasiewicz &u3d->local_setup_buff); 160490fccb52SAndrzej Pietrasiewicz } 160590fccb52SAndrzej Pietrasiewicz } 160690fccb52SAndrzej Pietrasiewicz } 160790fccb52SAndrzej Pietrasiewicz 160890fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->setuplock); 160990fccb52SAndrzej Pietrasiewicz } 161090fccb52SAndrzej Pietrasiewicz 161190fccb52SAndrzej Pietrasiewicz static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d) 161290fccb52SAndrzej Pietrasiewicz { 161390fccb52SAndrzej Pietrasiewicz u32 tmp, bit_pos; 161490fccb52SAndrzej Pietrasiewicz int i, ep_num = 0, direction = 0; 161590fccb52SAndrzej Pietrasiewicz struct mv_u3d_ep *curr_ep; 161690fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *curr_req, *temp_req; 161790fccb52SAndrzej Pietrasiewicz int status; 161890fccb52SAndrzej Pietrasiewicz 161990fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->vuc_regs->endcomplete); 162090fccb52SAndrzej Pietrasiewicz 162190fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp); 162290fccb52SAndrzej Pietrasiewicz if (!tmp) 162390fccb52SAndrzej Pietrasiewicz return; 162490fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->vuc_regs->endcomplete); 162590fccb52SAndrzej Pietrasiewicz 162690fccb52SAndrzej Pietrasiewicz for (i = 0; i < u3d->max_eps * 2; i++) { 162790fccb52SAndrzej Pietrasiewicz ep_num = i >> 1; 162890fccb52SAndrzej Pietrasiewicz direction = i % 2; 162990fccb52SAndrzej Pietrasiewicz 163090fccb52SAndrzej Pietrasiewicz bit_pos = 1 << (ep_num + 16 * direction); 163190fccb52SAndrzej Pietrasiewicz 163290fccb52SAndrzej Pietrasiewicz if (!(bit_pos & tmp)) 163390fccb52SAndrzej Pietrasiewicz continue; 163490fccb52SAndrzej Pietrasiewicz 163590fccb52SAndrzej Pietrasiewicz if (i == 0) 163690fccb52SAndrzej Pietrasiewicz curr_ep = &u3d->eps[1]; 163790fccb52SAndrzej Pietrasiewicz else 163890fccb52SAndrzej Pietrasiewicz curr_ep = &u3d->eps[i]; 163990fccb52SAndrzej Pietrasiewicz 164090fccb52SAndrzej Pietrasiewicz /* remove req out of ep request list after completion */ 164190fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "tr comp: check req_list\n"); 164290fccb52SAndrzej Pietrasiewicz spin_lock(&curr_ep->req_lock); 164390fccb52SAndrzej Pietrasiewicz if (!list_empty(&curr_ep->req_list)) { 164490fccb52SAndrzej Pietrasiewicz struct mv_u3d_req *req; 164590fccb52SAndrzej Pietrasiewicz req = list_entry(curr_ep->req_list.next, 164690fccb52SAndrzej Pietrasiewicz struct mv_u3d_req, list); 164790fccb52SAndrzej Pietrasiewicz list_del_init(&req->list); 164890fccb52SAndrzej Pietrasiewicz curr_ep->processing = 0; 164990fccb52SAndrzej Pietrasiewicz } 165090fccb52SAndrzej Pietrasiewicz spin_unlock(&curr_ep->req_lock); 165190fccb52SAndrzej Pietrasiewicz 165290fccb52SAndrzej Pietrasiewicz /* process the req queue until an uncomplete request */ 165390fccb52SAndrzej Pietrasiewicz list_for_each_entry_safe(curr_req, temp_req, 165490fccb52SAndrzej Pietrasiewicz &curr_ep->queue, queue) { 165590fccb52SAndrzej Pietrasiewicz status = mv_u3d_process_ep_req(u3d, i, curr_req); 165690fccb52SAndrzej Pietrasiewicz if (status) 165790fccb52SAndrzej Pietrasiewicz break; 165890fccb52SAndrzej Pietrasiewicz /* write back status to req */ 165990fccb52SAndrzej Pietrasiewicz curr_req->req.status = status; 166090fccb52SAndrzej Pietrasiewicz 166190fccb52SAndrzej Pietrasiewicz /* ep0 request completion */ 166290fccb52SAndrzej Pietrasiewicz if (ep_num == 0) { 166390fccb52SAndrzej Pietrasiewicz mv_u3d_done(curr_ep, curr_req, 0); 166490fccb52SAndrzej Pietrasiewicz break; 166590fccb52SAndrzej Pietrasiewicz } else { 166690fccb52SAndrzej Pietrasiewicz mv_u3d_done(curr_ep, curr_req, status); 166790fccb52SAndrzej Pietrasiewicz } 166890fccb52SAndrzej Pietrasiewicz } 166990fccb52SAndrzej Pietrasiewicz 167090fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n"); 167190fccb52SAndrzej Pietrasiewicz mv_u3d_start_queue(curr_ep); 167290fccb52SAndrzej Pietrasiewicz } 167390fccb52SAndrzej Pietrasiewicz } 167490fccb52SAndrzej Pietrasiewicz 167590fccb52SAndrzej Pietrasiewicz static irqreturn_t mv_u3d_irq(int irq, void *dev) 167690fccb52SAndrzej Pietrasiewicz { 167790fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = (struct mv_u3d *)dev; 167890fccb52SAndrzej Pietrasiewicz u32 status, intr; 167990fccb52SAndrzej Pietrasiewicz u32 bridgesetting; 168090fccb52SAndrzej Pietrasiewicz u32 trbunderrun; 168190fccb52SAndrzej Pietrasiewicz 168290fccb52SAndrzej Pietrasiewicz spin_lock(&u3d->lock); 168390fccb52SAndrzej Pietrasiewicz 168490fccb52SAndrzej Pietrasiewicz status = ioread32(&u3d->vuc_regs->intrcause); 168590fccb52SAndrzej Pietrasiewicz intr = ioread32(&u3d->vuc_regs->intrenable); 168690fccb52SAndrzej Pietrasiewicz status &= intr; 168790fccb52SAndrzej Pietrasiewicz 168890fccb52SAndrzej Pietrasiewicz if (status == 0) { 168990fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 169090fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "irq error!\n"); 169190fccb52SAndrzej Pietrasiewicz return IRQ_NONE; 169290fccb52SAndrzej Pietrasiewicz } 169390fccb52SAndrzej Pietrasiewicz 169490fccb52SAndrzej Pietrasiewicz if (status & MV_U3D_USBINT_VBUS_VALID) { 169590fccb52SAndrzej Pietrasiewicz bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting); 169690fccb52SAndrzej Pietrasiewicz if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) { 169790fccb52SAndrzej Pietrasiewicz /* write vbus valid bit of bridge setting to clear */ 169890fccb52SAndrzej Pietrasiewicz bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID; 169990fccb52SAndrzej Pietrasiewicz iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting); 170090fccb52SAndrzej Pietrasiewicz dev_dbg(u3d->dev, "vbus valid\n"); 170190fccb52SAndrzej Pietrasiewicz 170290fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_POWERED; 170390fccb52SAndrzej Pietrasiewicz u3d->vbus_valid_detect = 0; 170490fccb52SAndrzej Pietrasiewicz /* if external vbus detect is not supported, 170590fccb52SAndrzej Pietrasiewicz * we handle it here. 170690fccb52SAndrzej Pietrasiewicz */ 170790fccb52SAndrzej Pietrasiewicz if (!u3d->vbus) { 170890fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 170990fccb52SAndrzej Pietrasiewicz mv_u3d_vbus_session(&u3d->gadget, 1); 171090fccb52SAndrzej Pietrasiewicz spin_lock(&u3d->lock); 171190fccb52SAndrzej Pietrasiewicz } 171290fccb52SAndrzej Pietrasiewicz } else 171390fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "vbus bit is not set\n"); 171490fccb52SAndrzej Pietrasiewicz } 171590fccb52SAndrzej Pietrasiewicz 171690fccb52SAndrzej Pietrasiewicz /* RX data is already in the 16KB FIFO.*/ 171790fccb52SAndrzej Pietrasiewicz if (status & MV_U3D_USBINT_UNDER_RUN) { 171890fccb52SAndrzej Pietrasiewicz trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun); 171990fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "under run, ep%d\n", trbunderrun); 172090fccb52SAndrzej Pietrasiewicz iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun); 172190fccb52SAndrzej Pietrasiewicz mv_u3d_irq_process_error(u3d); 172290fccb52SAndrzej Pietrasiewicz } 172390fccb52SAndrzej Pietrasiewicz 172490fccb52SAndrzej Pietrasiewicz if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) { 172590fccb52SAndrzej Pietrasiewicz /* write one to clear */ 172690fccb52SAndrzej Pietrasiewicz iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR 172790fccb52SAndrzej Pietrasiewicz | MV_U3D_USBINT_TXDESC_ERR), 172890fccb52SAndrzej Pietrasiewicz &u3d->vuc_regs->intrcause); 172990fccb52SAndrzej Pietrasiewicz dev_err(u3d->dev, "desc err 0x%x\n", status); 173090fccb52SAndrzej Pietrasiewicz mv_u3d_irq_process_error(u3d); 173190fccb52SAndrzej Pietrasiewicz } 173290fccb52SAndrzej Pietrasiewicz 173390fccb52SAndrzej Pietrasiewicz if (status & MV_U3D_USBINT_LINK_CHG) 173490fccb52SAndrzej Pietrasiewicz mv_u3d_irq_process_link_change(u3d); 173590fccb52SAndrzej Pietrasiewicz 173690fccb52SAndrzej Pietrasiewicz if (status & MV_U3D_USBINT_TX_COMPLETE) 173790fccb52SAndrzej Pietrasiewicz mv_u3d_irq_process_tr_complete(u3d); 173890fccb52SAndrzej Pietrasiewicz 173990fccb52SAndrzej Pietrasiewicz if (status & MV_U3D_USBINT_RX_COMPLETE) 174090fccb52SAndrzej Pietrasiewicz mv_u3d_irq_process_tr_complete(u3d); 174190fccb52SAndrzej Pietrasiewicz 174290fccb52SAndrzej Pietrasiewicz if (status & MV_U3D_USBINT_SETUP) 174390fccb52SAndrzej Pietrasiewicz mv_u3d_irq_process_setup(u3d); 174490fccb52SAndrzej Pietrasiewicz 174590fccb52SAndrzej Pietrasiewicz spin_unlock(&u3d->lock); 174690fccb52SAndrzej Pietrasiewicz return IRQ_HANDLED; 174790fccb52SAndrzej Pietrasiewicz } 174890fccb52SAndrzej Pietrasiewicz 1749*d5d4b4f2SUwe Kleine-König static void mv_u3d_remove(struct platform_device *dev) 175090fccb52SAndrzej Pietrasiewicz { 175190fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = platform_get_drvdata(dev); 175290fccb52SAndrzej Pietrasiewicz 175390fccb52SAndrzej Pietrasiewicz BUG_ON(u3d == NULL); 175490fccb52SAndrzej Pietrasiewicz 175590fccb52SAndrzej Pietrasiewicz usb_del_gadget_udc(&u3d->gadget); 175690fccb52SAndrzej Pietrasiewicz 175790fccb52SAndrzej Pietrasiewicz /* free memory allocated in probe */ 175890fccb52SAndrzej Pietrasiewicz dma_pool_destroy(u3d->trb_pool); 175990fccb52SAndrzej Pietrasiewicz 176090fccb52SAndrzej Pietrasiewicz if (u3d->ep_context) 176190fccb52SAndrzej Pietrasiewicz dma_free_coherent(&dev->dev, u3d->ep_context_size, 176290fccb52SAndrzej Pietrasiewicz u3d->ep_context, u3d->ep_context_dma); 176390fccb52SAndrzej Pietrasiewicz 176490fccb52SAndrzej Pietrasiewicz kfree(u3d->eps); 176590fccb52SAndrzej Pietrasiewicz 176690fccb52SAndrzej Pietrasiewicz if (u3d->irq) 176790fccb52SAndrzej Pietrasiewicz free_irq(u3d->irq, u3d); 176890fccb52SAndrzej Pietrasiewicz 176990fccb52SAndrzej Pietrasiewicz if (u3d->cap_regs) 177090fccb52SAndrzej Pietrasiewicz iounmap(u3d->cap_regs); 177190fccb52SAndrzej Pietrasiewicz u3d->cap_regs = NULL; 177290fccb52SAndrzej Pietrasiewicz 177390fccb52SAndrzej Pietrasiewicz kfree(u3d->status_req); 177490fccb52SAndrzej Pietrasiewicz 177590fccb52SAndrzej Pietrasiewicz clk_put(u3d->clk); 177690fccb52SAndrzej Pietrasiewicz 177790fccb52SAndrzej Pietrasiewicz kfree(u3d); 177890fccb52SAndrzej Pietrasiewicz } 177990fccb52SAndrzej Pietrasiewicz 178090fccb52SAndrzej Pietrasiewicz static int mv_u3d_probe(struct platform_device *dev) 178190fccb52SAndrzej Pietrasiewicz { 178290fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = NULL; 178390fccb52SAndrzej Pietrasiewicz struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev); 178490fccb52SAndrzej Pietrasiewicz int retval = 0; 178590fccb52SAndrzej Pietrasiewicz struct resource *r; 178690fccb52SAndrzej Pietrasiewicz size_t size; 178790fccb52SAndrzej Pietrasiewicz 178890fccb52SAndrzej Pietrasiewicz if (!dev_get_platdata(&dev->dev)) { 178990fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "missing platform_data\n"); 179090fccb52SAndrzej Pietrasiewicz retval = -ENODEV; 179190fccb52SAndrzej Pietrasiewicz goto err_pdata; 179290fccb52SAndrzej Pietrasiewicz } 179390fccb52SAndrzej Pietrasiewicz 179490fccb52SAndrzej Pietrasiewicz u3d = kzalloc(sizeof(*u3d), GFP_KERNEL); 179590fccb52SAndrzej Pietrasiewicz if (!u3d) { 179690fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 179790fccb52SAndrzej Pietrasiewicz goto err_alloc_private; 179890fccb52SAndrzej Pietrasiewicz } 179990fccb52SAndrzej Pietrasiewicz 180090fccb52SAndrzej Pietrasiewicz spin_lock_init(&u3d->lock); 180190fccb52SAndrzej Pietrasiewicz 180290fccb52SAndrzej Pietrasiewicz platform_set_drvdata(dev, u3d); 180390fccb52SAndrzej Pietrasiewicz 180490fccb52SAndrzej Pietrasiewicz u3d->dev = &dev->dev; 180590fccb52SAndrzej Pietrasiewicz u3d->vbus = pdata->vbus; 180690fccb52SAndrzej Pietrasiewicz 180790fccb52SAndrzej Pietrasiewicz u3d->clk = clk_get(&dev->dev, NULL); 180890fccb52SAndrzej Pietrasiewicz if (IS_ERR(u3d->clk)) { 180990fccb52SAndrzej Pietrasiewicz retval = PTR_ERR(u3d->clk); 181090fccb52SAndrzej Pietrasiewicz goto err_get_clk; 181190fccb52SAndrzej Pietrasiewicz } 181290fccb52SAndrzej Pietrasiewicz 181390fccb52SAndrzej Pietrasiewicz r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs"); 181490fccb52SAndrzej Pietrasiewicz if (!r) { 181590fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "no I/O memory resource defined\n"); 181690fccb52SAndrzej Pietrasiewicz retval = -ENODEV; 181790fccb52SAndrzej Pietrasiewicz goto err_get_cap_regs; 181890fccb52SAndrzej Pietrasiewicz } 181990fccb52SAndrzej Pietrasiewicz 182090fccb52SAndrzej Pietrasiewicz u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *) 182190fccb52SAndrzej Pietrasiewicz ioremap(r->start, resource_size(r)); 182290fccb52SAndrzej Pietrasiewicz if (!u3d->cap_regs) { 182390fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "failed to map I/O memory\n"); 182490fccb52SAndrzej Pietrasiewicz retval = -EBUSY; 182590fccb52SAndrzej Pietrasiewicz goto err_map_cap_regs; 182690fccb52SAndrzej Pietrasiewicz } else { 182790fccb52SAndrzej Pietrasiewicz dev_dbg(&dev->dev, "cap_regs address: 0x%lx/0x%lx\n", 182890fccb52SAndrzej Pietrasiewicz (unsigned long) r->start, 182990fccb52SAndrzej Pietrasiewicz (unsigned long) u3d->cap_regs); 183090fccb52SAndrzej Pietrasiewicz } 183190fccb52SAndrzej Pietrasiewicz 183290fccb52SAndrzej Pietrasiewicz /* we will access controller register, so enable the u3d controller */ 1833374a1020SAlexey Khoroshilov retval = clk_enable(u3d->clk); 1834374a1020SAlexey Khoroshilov if (retval) { 1835374a1020SAlexey Khoroshilov dev_err(&dev->dev, "clk_enable error %d\n", retval); 1836374a1020SAlexey Khoroshilov goto err_u3d_enable; 1837374a1020SAlexey Khoroshilov } 183890fccb52SAndrzej Pietrasiewicz 183990fccb52SAndrzej Pietrasiewicz if (pdata->phy_init) { 184090fccb52SAndrzej Pietrasiewicz retval = pdata->phy_init(u3d->phy_regs); 184190fccb52SAndrzej Pietrasiewicz if (retval) { 184290fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "init phy error %d\n", retval); 1843374a1020SAlexey Khoroshilov clk_disable(u3d->clk); 1844374a1020SAlexey Khoroshilov goto err_phy_init; 184590fccb52SAndrzej Pietrasiewicz } 184690fccb52SAndrzej Pietrasiewicz } 184790fccb52SAndrzej Pietrasiewicz 184890fccb52SAndrzej Pietrasiewicz u3d->op_regs = (struct mv_u3d_op_regs __iomem *)(u3d->cap_regs 184990fccb52SAndrzej Pietrasiewicz + MV_U3D_USB3_OP_REGS_OFFSET); 185090fccb52SAndrzej Pietrasiewicz 185190fccb52SAndrzej Pietrasiewicz u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)(u3d->cap_regs 185290fccb52SAndrzej Pietrasiewicz + ioread32(&u3d->cap_regs->vuoff)); 185390fccb52SAndrzej Pietrasiewicz 185490fccb52SAndrzej Pietrasiewicz u3d->max_eps = 16; 185590fccb52SAndrzej Pietrasiewicz 185690fccb52SAndrzej Pietrasiewicz /* 185790fccb52SAndrzej Pietrasiewicz * some platform will use usb to download image, it may not disconnect 185890fccb52SAndrzej Pietrasiewicz * usb gadget before loading kernel. So first stop u3d here. 185990fccb52SAndrzej Pietrasiewicz */ 186090fccb52SAndrzej Pietrasiewicz mv_u3d_controller_stop(u3d); 186190fccb52SAndrzej Pietrasiewicz iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause); 186290fccb52SAndrzej Pietrasiewicz 186390fccb52SAndrzej Pietrasiewicz if (pdata->phy_deinit) 186490fccb52SAndrzej Pietrasiewicz pdata->phy_deinit(u3d->phy_regs); 186590fccb52SAndrzej Pietrasiewicz clk_disable(u3d->clk); 186690fccb52SAndrzej Pietrasiewicz 186790fccb52SAndrzej Pietrasiewicz size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2; 186890fccb52SAndrzej Pietrasiewicz size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1) 186990fccb52SAndrzej Pietrasiewicz & ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1); 187090fccb52SAndrzej Pietrasiewicz u3d->ep_context = dma_alloc_coherent(&dev->dev, size, 187190fccb52SAndrzej Pietrasiewicz &u3d->ep_context_dma, GFP_KERNEL); 187290fccb52SAndrzej Pietrasiewicz if (!u3d->ep_context) { 187390fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "allocate ep context memory failed\n"); 187490fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 187590fccb52SAndrzej Pietrasiewicz goto err_alloc_ep_context; 187690fccb52SAndrzej Pietrasiewicz } 187790fccb52SAndrzej Pietrasiewicz u3d->ep_context_size = size; 187890fccb52SAndrzej Pietrasiewicz 187990fccb52SAndrzej Pietrasiewicz /* create TRB dma_pool resource */ 188090fccb52SAndrzej Pietrasiewicz u3d->trb_pool = dma_pool_create("u3d_trb", 188190fccb52SAndrzej Pietrasiewicz &dev->dev, 188290fccb52SAndrzej Pietrasiewicz sizeof(struct mv_u3d_trb_hw), 188390fccb52SAndrzej Pietrasiewicz MV_U3D_TRB_ALIGNMENT, 188490fccb52SAndrzej Pietrasiewicz MV_U3D_DMA_BOUNDARY); 188590fccb52SAndrzej Pietrasiewicz 188690fccb52SAndrzej Pietrasiewicz if (!u3d->trb_pool) { 188790fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 188890fccb52SAndrzej Pietrasiewicz goto err_alloc_trb_pool; 188990fccb52SAndrzej Pietrasiewicz } 189090fccb52SAndrzej Pietrasiewicz 189190fccb52SAndrzej Pietrasiewicz size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2; 189290fccb52SAndrzej Pietrasiewicz u3d->eps = kzalloc(size, GFP_KERNEL); 189390fccb52SAndrzej Pietrasiewicz if (!u3d->eps) { 189490fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 189590fccb52SAndrzej Pietrasiewicz goto err_alloc_eps; 189690fccb52SAndrzej Pietrasiewicz } 189790fccb52SAndrzej Pietrasiewicz 189890fccb52SAndrzej Pietrasiewicz /* initialize ep0 status request structure */ 189990fccb52SAndrzej Pietrasiewicz u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL); 190090fccb52SAndrzej Pietrasiewicz if (!u3d->status_req) { 190190fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 190290fccb52SAndrzej Pietrasiewicz goto err_alloc_status_req; 190390fccb52SAndrzej Pietrasiewicz } 190490fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&u3d->status_req->queue); 190590fccb52SAndrzej Pietrasiewicz 190690fccb52SAndrzej Pietrasiewicz /* allocate a small amount of memory to get valid address */ 190790fccb52SAndrzej Pietrasiewicz u3d->status_req->req.buf = (char *)u3d->status_req 190890fccb52SAndrzej Pietrasiewicz + sizeof(struct mv_u3d_req); 190990fccb52SAndrzej Pietrasiewicz u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf); 191090fccb52SAndrzej Pietrasiewicz 191190fccb52SAndrzej Pietrasiewicz u3d->resume_state = USB_STATE_NOTATTACHED; 191290fccb52SAndrzej Pietrasiewicz u3d->usb_state = USB_STATE_ATTACHED; 191390fccb52SAndrzej Pietrasiewicz u3d->ep0_dir = MV_U3D_EP_DIR_OUT; 191490fccb52SAndrzej Pietrasiewicz u3d->remote_wakeup = 0; 191590fccb52SAndrzej Pietrasiewicz 191690fccb52SAndrzej Pietrasiewicz r = platform_get_resource(dev, IORESOURCE_IRQ, 0); 191790fccb52SAndrzej Pietrasiewicz if (!r) { 191890fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "no IRQ resource defined\n"); 191990fccb52SAndrzej Pietrasiewicz retval = -ENODEV; 192090fccb52SAndrzej Pietrasiewicz goto err_get_irq; 192190fccb52SAndrzej Pietrasiewicz } 192290fccb52SAndrzej Pietrasiewicz u3d->irq = r->start; 192390fccb52SAndrzej Pietrasiewicz 192490fccb52SAndrzej Pietrasiewicz /* initialize gadget structure */ 192590fccb52SAndrzej Pietrasiewicz u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */ 192690fccb52SAndrzej Pietrasiewicz u3d->gadget.ep0 = &u3d->eps[1].ep; /* gadget ep0 */ 192790fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&u3d->gadget.ep_list); /* ep_list */ 192890fccb52SAndrzej Pietrasiewicz u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ 192990fccb52SAndrzej Pietrasiewicz 193090fccb52SAndrzej Pietrasiewicz /* the "gadget" abstracts/virtualizes the controller */ 193190fccb52SAndrzej Pietrasiewicz u3d->gadget.name = driver_name; /* gadget name */ 193290fccb52SAndrzej Pietrasiewicz 193390fccb52SAndrzej Pietrasiewicz mv_u3d_eps_init(u3d); 193490fccb52SAndrzej Pietrasiewicz 19352af0c5ffSNadezda Lutovinova if (request_irq(u3d->irq, mv_u3d_irq, 19362af0c5ffSNadezda Lutovinova IRQF_SHARED, driver_name, u3d)) { 19372af0c5ffSNadezda Lutovinova u3d->irq = 0; 19382af0c5ffSNadezda Lutovinova dev_err(&dev->dev, "Request irq %d for u3d failed\n", 19392af0c5ffSNadezda Lutovinova u3d->irq); 19402af0c5ffSNadezda Lutovinova retval = -ENODEV; 19412af0c5ffSNadezda Lutovinova goto err_request_irq; 19422af0c5ffSNadezda Lutovinova } 19432af0c5ffSNadezda Lutovinova 194490fccb52SAndrzej Pietrasiewicz /* external vbus detection */ 194590fccb52SAndrzej Pietrasiewicz if (u3d->vbus) { 194690fccb52SAndrzej Pietrasiewicz u3d->clock_gating = 1; 194790fccb52SAndrzej Pietrasiewicz dev_err(&dev->dev, "external vbus detection\n"); 194890fccb52SAndrzej Pietrasiewicz } 194990fccb52SAndrzej Pietrasiewicz 195090fccb52SAndrzej Pietrasiewicz if (!u3d->clock_gating) 195190fccb52SAndrzej Pietrasiewicz u3d->vbus_active = 1; 195290fccb52SAndrzej Pietrasiewicz 195390fccb52SAndrzej Pietrasiewicz /* enable usb3 controller vbus detection */ 195490fccb52SAndrzej Pietrasiewicz u3d->vbus_valid_detect = 1; 195590fccb52SAndrzej Pietrasiewicz 195690fccb52SAndrzej Pietrasiewicz retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget); 195790fccb52SAndrzej Pietrasiewicz if (retval) 195890fccb52SAndrzej Pietrasiewicz goto err_unregister; 195990fccb52SAndrzej Pietrasiewicz 196090fccb52SAndrzej Pietrasiewicz dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n", 196190fccb52SAndrzej Pietrasiewicz u3d->clock_gating ? "with" : "without"); 196290fccb52SAndrzej Pietrasiewicz 196390fccb52SAndrzej Pietrasiewicz return 0; 196490fccb52SAndrzej Pietrasiewicz 196590fccb52SAndrzej Pietrasiewicz err_unregister: 196690fccb52SAndrzej Pietrasiewicz free_irq(u3d->irq, u3d); 196790fccb52SAndrzej Pietrasiewicz err_get_irq: 19682af0c5ffSNadezda Lutovinova err_request_irq: 196990fccb52SAndrzej Pietrasiewicz kfree(u3d->status_req); 197090fccb52SAndrzej Pietrasiewicz err_alloc_status_req: 197190fccb52SAndrzej Pietrasiewicz kfree(u3d->eps); 197290fccb52SAndrzej Pietrasiewicz err_alloc_eps: 197390fccb52SAndrzej Pietrasiewicz dma_pool_destroy(u3d->trb_pool); 197490fccb52SAndrzej Pietrasiewicz err_alloc_trb_pool: 197590fccb52SAndrzej Pietrasiewicz dma_free_coherent(&dev->dev, u3d->ep_context_size, 197690fccb52SAndrzej Pietrasiewicz u3d->ep_context, u3d->ep_context_dma); 197790fccb52SAndrzej Pietrasiewicz err_alloc_ep_context: 1978374a1020SAlexey Khoroshilov err_phy_init: 197990fccb52SAndrzej Pietrasiewicz err_u3d_enable: 198090fccb52SAndrzej Pietrasiewicz iounmap(u3d->cap_regs); 198190fccb52SAndrzej Pietrasiewicz err_map_cap_regs: 198290fccb52SAndrzej Pietrasiewicz err_get_cap_regs: 198390fccb52SAndrzej Pietrasiewicz clk_put(u3d->clk); 1984374a1020SAlexey Khoroshilov err_get_clk: 198590fccb52SAndrzej Pietrasiewicz kfree(u3d); 198690fccb52SAndrzej Pietrasiewicz err_alloc_private: 198790fccb52SAndrzej Pietrasiewicz err_pdata: 198890fccb52SAndrzej Pietrasiewicz return retval; 198990fccb52SAndrzej Pietrasiewicz } 199090fccb52SAndrzej Pietrasiewicz 199190fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_PM_SLEEP 199290fccb52SAndrzej Pietrasiewicz static int mv_u3d_suspend(struct device *dev) 199390fccb52SAndrzej Pietrasiewicz { 199490fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = dev_get_drvdata(dev); 199590fccb52SAndrzej Pietrasiewicz 199690fccb52SAndrzej Pietrasiewicz /* 199790fccb52SAndrzej Pietrasiewicz * only cable is unplugged, usb can suspend. 199890fccb52SAndrzej Pietrasiewicz * So do not care about clock_gating == 1, it is handled by 199990fccb52SAndrzej Pietrasiewicz * vbus session. 200090fccb52SAndrzej Pietrasiewicz */ 200190fccb52SAndrzej Pietrasiewicz if (!u3d->clock_gating) { 200290fccb52SAndrzej Pietrasiewicz mv_u3d_controller_stop(u3d); 200390fccb52SAndrzej Pietrasiewicz 200490fccb52SAndrzej Pietrasiewicz spin_lock_irq(&u3d->lock); 200590fccb52SAndrzej Pietrasiewicz /* stop all usb activities */ 200690fccb52SAndrzej Pietrasiewicz mv_u3d_stop_activity(u3d, u3d->driver); 200790fccb52SAndrzej Pietrasiewicz spin_unlock_irq(&u3d->lock); 200890fccb52SAndrzej Pietrasiewicz 200990fccb52SAndrzej Pietrasiewicz mv_u3d_disable(u3d); 201090fccb52SAndrzej Pietrasiewicz } 201190fccb52SAndrzej Pietrasiewicz 201290fccb52SAndrzej Pietrasiewicz return 0; 201390fccb52SAndrzej Pietrasiewicz } 201490fccb52SAndrzej Pietrasiewicz 201590fccb52SAndrzej Pietrasiewicz static int mv_u3d_resume(struct device *dev) 201690fccb52SAndrzej Pietrasiewicz { 201790fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = dev_get_drvdata(dev); 201890fccb52SAndrzej Pietrasiewicz int retval; 201990fccb52SAndrzej Pietrasiewicz 202090fccb52SAndrzej Pietrasiewicz if (!u3d->clock_gating) { 202190fccb52SAndrzej Pietrasiewicz retval = mv_u3d_enable(u3d); 202290fccb52SAndrzej Pietrasiewicz if (retval) 202390fccb52SAndrzej Pietrasiewicz return retval; 202490fccb52SAndrzej Pietrasiewicz 202590fccb52SAndrzej Pietrasiewicz if (u3d->driver && u3d->softconnect) { 202690fccb52SAndrzej Pietrasiewicz mv_u3d_controller_reset(u3d); 202790fccb52SAndrzej Pietrasiewicz mv_u3d_ep0_reset(u3d); 202890fccb52SAndrzej Pietrasiewicz mv_u3d_controller_start(u3d); 202990fccb52SAndrzej Pietrasiewicz } 203090fccb52SAndrzej Pietrasiewicz } 203190fccb52SAndrzej Pietrasiewicz 203290fccb52SAndrzej Pietrasiewicz return 0; 203390fccb52SAndrzej Pietrasiewicz } 203490fccb52SAndrzej Pietrasiewicz #endif 203590fccb52SAndrzej Pietrasiewicz 203690fccb52SAndrzej Pietrasiewicz static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); 203790fccb52SAndrzej Pietrasiewicz 203890fccb52SAndrzej Pietrasiewicz static void mv_u3d_shutdown(struct platform_device *dev) 203990fccb52SAndrzej Pietrasiewicz { 204090fccb52SAndrzej Pietrasiewicz struct mv_u3d *u3d = platform_get_drvdata(dev); 204190fccb52SAndrzej Pietrasiewicz u32 tmp; 204290fccb52SAndrzej Pietrasiewicz 204390fccb52SAndrzej Pietrasiewicz tmp = ioread32(&u3d->op_regs->usbcmd); 204490fccb52SAndrzej Pietrasiewicz tmp &= ~MV_U3D_CMD_RUN_STOP; 204590fccb52SAndrzej Pietrasiewicz iowrite32(tmp, &u3d->op_regs->usbcmd); 204690fccb52SAndrzej Pietrasiewicz } 204790fccb52SAndrzej Pietrasiewicz 204890fccb52SAndrzej Pietrasiewicz static struct platform_driver mv_u3d_driver = { 204990fccb52SAndrzej Pietrasiewicz .probe = mv_u3d_probe, 2050*d5d4b4f2SUwe Kleine-König .remove_new = mv_u3d_remove, 205190fccb52SAndrzej Pietrasiewicz .shutdown = mv_u3d_shutdown, 205290fccb52SAndrzej Pietrasiewicz .driver = { 205390fccb52SAndrzej Pietrasiewicz .name = "mv-u3d", 205490fccb52SAndrzej Pietrasiewicz .pm = &mv_u3d_pm_ops, 205590fccb52SAndrzej Pietrasiewicz }, 205690fccb52SAndrzej Pietrasiewicz }; 205790fccb52SAndrzej Pietrasiewicz 205890fccb52SAndrzej Pietrasiewicz module_platform_driver(mv_u3d_driver); 205990fccb52SAndrzej Pietrasiewicz MODULE_ALIAS("platform:mv-u3d"); 206090fccb52SAndrzej Pietrasiewicz MODULE_DESCRIPTION(DRIVER_DESC); 206190fccb52SAndrzej Pietrasiewicz MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>"); 206290fccb52SAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 2063