📄 pxa2xx_udc.c
字号:
if (&req->req != _req) { local_irq_restore(flags); return -EINVAL; }#ifdef USE_DMA if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { cancel_dma(ep); done(ep, req, -ECONNRESET); /* restart i/o */ if (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct pxa2xx_request, queue); kick_dma(ep, req); } } else#endif done(ep, req, -ECONNRESET); local_irq_restore(flags); return 0;}/*-------------------------------------------------------------------------*/static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value){ struct pxa2xx_ep *ep; unsigned long flags; ep = container_of(_ep, struct pxa2xx_ep, ep); if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name)) || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { DMSG("%s, bad ep\n", __FUNCTION__); return -EINVAL; } if (value == 0) { /* this path (reset toggle+halt) is needed to implement * SET_INTERFACE on normal hardware. but it can't be * done from software on the PXA UDC, and the hardware * forgets to do it as part of SET_INTERFACE automagic. */ DMSG("only host can clear %s halt\n", _ep->name); return -EROFS; } local_irq_save(flags); if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 || !list_empty(&ep->queue))) { local_irq_restore(flags); return -EAGAIN; } /* FST bit is the same for control, bulk in, bulk out, interrupt in */ *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; /* ep0 needs special care */ if (!ep->desc) { start_watchdog(ep->dev); ep->dev->req_pending = 0; ep->dev->ep0state = EP0_STALL; /* and bulk/intr endpoints like dropping stalls too */ } else { unsigned i; for (i = 0; i < 1000; i += 20) { if (*ep->reg_udccs & UDCCS_BI_SST) break; udelay(20); } } local_irq_restore(flags); DBG(DBG_VERBOSE, "%s halt\n", _ep->name); return 0;}static int pxa2xx_ep_fifo_status(struct usb_ep *_ep){ struct pxa2xx_ep *ep; ep = container_of(_ep, struct pxa2xx_ep, ep); if (!_ep) { DMSG("%s, bad ep\n", __FUNCTION__); return -ENODEV; } /* pxa can't report unclaimed bytes from IN fifos */ if ((ep->bEndpointAddress & USB_DIR_IN) != 0) return -EOPNOTSUPP; if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) return 0; else return (*ep->reg_ubcr & 0xfff) + 1;}static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep){ struct pxa2xx_ep *ep; ep = container_of(_ep, struct pxa2xx_ep, ep); if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { DMSG("%s, bad ep\n", __FUNCTION__); return; } /* toggle and halt bits stay unchanged */ /* for OUT, just read and discard the FIFO contents. */ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) (void) *ep->reg_uddr; return; } /* most IN status is the same, but ISO can't stall */ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) ? 0 : UDCCS_BI_SST;}static struct usb_ep_ops pxa2xx_ep_ops = { .enable = pxa2xx_ep_enable, .disable = pxa2xx_ep_disable, .alloc_request = pxa2xx_ep_alloc_request, .free_request = pxa2xx_ep_free_request, .alloc_buffer = pxa2xx_ep_alloc_buffer, .free_buffer = pxa2xx_ep_free_buffer, .queue = pxa2xx_ep_queue, .dequeue = pxa2xx_ep_dequeue, .set_halt = pxa2xx_ep_set_halt, .fifo_status = pxa2xx_ep_fifo_status, .fifo_flush = pxa2xx_ep_fifo_flush,};/* --------------------------------------------------------------------------- * device-scoped parts of the api to the usb controller hardware * --------------------------------------------------------------------------- */static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget){ return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);}static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget){ /* host may not have enabled remote wakeup */ if ((UDCCS0 & UDCCS0_DRWF) == 0) return -EHOSTUNREACH; udc_set_mask_UDCCR(UDCCR_RSM); return 0;}static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *);static void udc_enable (struct pxa2xx_udc *);static void udc_disable(struct pxa2xx_udc *);/* We disable the UDC -- and its 48 MHz clock -- whenever it's not * in active use. */static int pullup(struct pxa2xx_udc *udc, int is_active){ is_active = is_active && udc->vbus && udc->pullup; DMSG("%s\n", is_active ? "active" : "inactive"); if (is_active) udc_enable(udc); else { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { DMSG("disconnect %s\n", udc->driver ? udc->driver->driver.name : "(no driver)"); stop_activity(udc, udc->driver); } udc_disable(udc); } return 0;}/* VBUS reporting logically comes from a transceiver */static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active){ struct pxa2xx_udc *udc; udc = container_of(_gadget, struct pxa2xx_udc, gadget); udc->vbus = is_active = (is_active != 0); DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); pullup(udc, is_active); return 0;}/* drivers may have software control over D+ pullup */static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active){ struct pxa2xx_udc *udc; udc = container_of(_gadget, struct pxa2xx_udc, gadget); /* not all boards support pullup control */ if (!udc->mach->udc_command) return -EOPNOTSUPP; is_active = (is_active != 0); udc->pullup = is_active; pullup(udc, is_active); return 0;}static const struct usb_gadget_ops pxa2xx_udc_ops = { .get_frame = pxa2xx_udc_get_frame, .wakeup = pxa2xx_udc_wakeup, .vbus_session = pxa2xx_udc_vbus_session, .pullup = pxa2xx_udc_pullup, // .vbus_draw ... boards may consume current from VBUS, up to // 100-500mA based on config. the 500uA suspend ceiling means // that exclusively vbus-powered PXA designs violate USB specs.};/*-------------------------------------------------------------------------*/#ifdef CONFIG_USB_GADGET_DEBUG_FILESstatic const char proc_node_name [] = "driver/udc";static intudc_proc_read(char *page, char **start, off_t off, int count, int *eof, void *_dev){ char *buf = page; struct pxa2xx_udc *dev = _dev; char *next = buf; unsigned size = count; unsigned long flags; int i, t; u32 tmp; if (off != 0) return 0; local_irq_save(flags); /* basic device status */ t = scnprintf(next, size, DRIVER_DESC "\n" "%s version: %s\nGadget driver: %s\nHost %s\n\n", driver_name, DRIVER_VERSION SIZE_STR DMASTR, dev->driver ? dev->driver->driver.name : "(none)", is_vbus_present() ? "full speed" : "disconnected"); size -= t; next += t; /* registers for device and ep0 */ t = scnprintf(next, size, "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); size -= t; next += t; tmp = UDCCR; t = scnprintf(next, size, "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, (tmp & UDCCR_REM) ? " rem" : "", (tmp & UDCCR_RSTIR) ? " rstir" : "", (tmp & UDCCR_SRM) ? " srm" : "", (tmp & UDCCR_SUSIR) ? " susir" : "", (tmp & UDCCR_RESIR) ? " resir" : "", (tmp & UDCCR_RSM) ? " rsm" : "", (tmp & UDCCR_UDA) ? " uda" : "", (tmp & UDCCR_UDE) ? " ude" : ""); size -= t; next += t; tmp = UDCCS0; t = scnprintf(next, size, "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, (tmp & UDCCS0_SA) ? " sa" : "", (tmp & UDCCS0_RNE) ? " rne" : "", (tmp & UDCCS0_FST) ? " fst" : "", (tmp & UDCCS0_SST) ? " sst" : "", (tmp & UDCCS0_DRWF) ? " dwrf" : "", (tmp & UDCCS0_FTF) ? " ftf" : "", (tmp & UDCCS0_IPR) ? " ipr" : "", (tmp & UDCCS0_OPR) ? " opr" : ""); size -= t; next += t; if (dev->has_cfr) { tmp = UDCCFR; t = scnprintf(next, size, "udccfr %02X =%s%s\n", tmp, (tmp & UDCCFR_AREN) ? " aren" : "", (tmp & UDCCFR_ACM) ? " acm" : ""); size -= t; next += t; } if (!is_vbus_present() || !dev->driver) goto done; t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", dev->stats.write.bytes, dev->stats.write.ops, dev->stats.read.bytes, dev->stats.read.ops, dev->stats.irqs); size -= t; next += t; /* dump endpoint queues */ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep [i]; struct pxa2xx_request *req; int t; if (i != 0) { const struct usb_endpoint_descriptor *d; d = ep->desc; if (!d) continue; tmp = *dev->ep [i].reg_udccs; t = scnprintf(next, size, "%s max %d %s udccs %02x irqs %lu/%lu\n", ep->ep.name, le16_to_cpu (d->wMaxPacketSize), (ep->dma >= 0) ? "dma" : "pio", tmp, ep->pio_irqs, ep->dma_irqs); /* TODO translate all five groups of udccs bits! */ } else /* ep0 should only have one transfer queued */ t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n", ep->pio_irqs); if (t <= 0 || t > size) goto done; size -= t; next += t; if (list_empty(&ep->queue)) { t = scnprintf(next, size, "\t(nothing queued)\n"); if (t <= 0 || t > size) goto done; size -= t; next += t; continue; } list_for_each_entry(req, &ep->queue, queue) {#ifdef USE_DMA if (ep->dma >= 0 && req->queue.prev == &ep->queue) t = scnprintf(next, size, "\treq %p len %d/%d " "buf %p (dma%d dcmd %08x)\n", &req->req, req->req.actual, req->req.length, req->req.buf, ep->dma, DCMD(ep->dma) // low 13 bits == bytes-to-go ); else#endif t = scnprintf(next, size, "\treq %p len %d/%d buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); if (t <= 0 || t > size) goto done; size -= t; next += t; } }done: local_irq_restore(flags); *eof = 1; return count - size;}#define create_proc_files() \ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)#define remove_proc_files() \ remove_proc_entry(proc_node_name, NULL)#else /* !CONFIG_USB_GADGET_DEBUG_FILES */#define create_proc_files() do {} while (0)#define remove_proc_files() do {} while (0)#endif /* CONFIG_USB_GADGET_DEBUG_FILES *//* "function" sysfs attribute */static ssize_tshow_function (struct device *_dev, struct device_attribute *attr, char *buf){ struct pxa2xx_udc *dev = dev_get_drvdata (_dev); if (!dev->driver || !dev->driver->function || strlen (dev->driver->function) > PAGE_SIZE) return 0; return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);}static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);/*-------------------------------------------------------------------------*//* * udc_disable - disable USB device controller */static void udc_disable(struct pxa2xx_udc *dev){ /* block all irqs */ udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); UICR0 = UICR1 = 0xff; UFNRH = UFNRH_SIM; /* if hardware supports it, disconnect from usb */ pullup_off(); udc_clear_mask_UDCCR(UDCCR_UDE);#ifdef CONFIG_ARCH_PXA /* Disable clock for USB device */ pxa_set_cken(CKEN11_USB, 0);#endif ep0_idle (dev); dev->gadget.speed = USB_SPEED_UNKNOWN; LED_CONNECTED_OFF;}/* * udc_reinit - initialize software state */static void udc_reinit(struct pxa2xx_udc *dev){ u32 i; /* device/ep0 records init */ INIT_LIST_HEAD (&dev->gadget.ep_list); INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); dev->ep0state = EP0_IDLE; /* basic endpoint records init */ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep[i]; if (i != 0) list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ep->desc = NULL; ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); ep->pio_irqs = ep->dma_irqs = 0; } /* the rest was statically initialized, and is read-only */}/* until it's enabled, this UDC should be completely invisible * to any USB host. */static void udc_enable (struct pxa2xx_udc *dev){ udc_clear_mask_UDCCR(UDCCR_UDE);#ifdef CONFIG_ARCH_PXA /* Enable clock for USB device */ pxa_set_cken(CKEN11_USB, 1); udelay(5);#endif /* try to clear these bits before we enable the udc */ udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); ep0_idle(dev); dev->gadget.speed = USB_SPEED_UNKNOWN; dev->stats.irqs = 0; /* * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: * - enable UDC * - if RESET is already in progress, ack interrupt * - unmask reset interrupt */ udc_set_mask_UDCCR(UDCCR_UDE); if (!(UDCCR & UDCCR_UDA)) udc_ack_int_UDCCR(UDCCR_RSTIR); if (dev->has_cfr /* UDC_RES2 is defined */) { /* pxa255 (a0+) can avoid a set_config race that could * prevent gadget drivers from configuring correctly */ UDCCFR = UDCCFR_ACM | UDCCFR_MB1; } else { /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) * which could result in missing packets and interrupts. * supposedly one bit per endpoint, controlling whether it * double buffers or not; ACM/AREN bits fit into the holes. * zero bits (like USIR0_IRx) disable double buffering. */ UDC_RES1 = 0x00; UDC_RES2 = 0x00; }#ifdef DISABLE_TEST_MODE /* "test mode" seems to have become the default in later chip * revs, preventing double buffering (and invalidating docs). * this EXPERIMENT enables it for bulk endpoints by tweaking * undefined/reserved register bits (that other drivers clear). * Belcarra code comments noted this usage. */ if (fifo_mode & 1) { /* IN endpoints */ UDC_RES1 |= USIR0_IR1|USIR0_IR6; UDC_RES2 |= USIR1_IR11; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -