// SPDX-License-Identifier: GPL-2.0 /* * Toshiba TC86C001 ("Goku-S") USB Device Controller driver * * Copyright (C) 2000-2002 Lineo * by Stuart Lynne, Tom Rushworth, and Bruce Balden * Copyright (C) 2002 Toshiba Corporation * Copyright (C) 2003 MontaVista Software (source@mvista.com)
*/
/* * This device has ep0 and three semi-configurable bulk/interrupt endpoints. * * - Endpoint numbering is fixed: ep{1,2,3}-bulk * - Gadget drivers can choose ep maxpacket (8/16/32/64) * - Gadget drivers can choose direction (IN, OUT) * - DMA works with ep1 (OUT transfers) and ep2 (IN transfers).
*/
/* * IN dma behaves ok under testing, though the IN-dma abort paths don't * seem to behave quite as expected. Used by default. * * OUT dma documents design problems handling the common "short packet" * transfer termination policy; it couldn't be enabled by default, even * if the OUT-dma abort problems had a resolution.
*/ staticunsigned use_dma = 1;
#if 0 //#include <linux/moduleparam.h> /* "modprobe goku_udc use_dma=1" etc * 0 to disable dma * 1 to use IN dma only (normal operation) * 2 to use IN and OUT dma
*/
module_param(use_dma, uint, S_IRUGO); #endif
ep = container_of(_ep, struct goku_ep, ep); if (!_ep || !desc
|| desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL;
dev = ep->dev; if (ep == &dev->ep[0]) return -EINVAL; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; if (ep->num != usb_endpoint_num(desc)) return -EINVAL;
switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: break; default: return -EINVAL;
}
if ((readl(ep->reg_status) & EPxSTATUS_EP_MASK)
!= EPxSTATUS_EP_INVALID) return -EBUSY;
/* enabling the no-toggle interrupt mode would need an api hook */
mode = 0;
max = get_unaligned_le16(&desc->wMaxPacketSize); switch (max) { case 64:
mode++;
fallthrough; case 32:
mode++;
fallthrough; case 16:
mode++;
fallthrough; case 8:
mode <<= 3; break; default: return -EINVAL;
}
mode |= 2 << 1; /* bulk, or intr-with-toggle */
/* ep1/ep2 dma direction is chosen early; it works in the other * direction, with pio. be cautious with out-dma.
*/
ep->is_in = usb_endpoint_dir_in(desc); if (ep->is_in) {
mode |= 1;
ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT);
} else {
ep->dma = (use_dma == 2) && (ep->num == UDC_MSTWR_ENDPOINT); if (ep->dma)
DBG(dev, "%s out-dma hides short packets\n",
ep->ep.name);
}
spin_lock_irqsave(&ep->dev->lock, flags);
/* ep1 and ep2 can do double buffering and/or dma */ if (ep->num < 3) { struct goku_udc_regs __iomem *regs = ep->dev->regs;
u32 tmp;
/* double buffer except (for now) with pio in */
tmp = ((ep->dma || !ep->is_in)
? 0x10 /* double buffered */
: 0x11 /* single buffer */
) << ep->num;
tmp |= readl(®s->EPxSingle);
writel(tmp, ®s->EPxSingle);
/* usually do nothing without an OUT packet */ if (likely(ep->num != 0 || bufferspace != 0)) { if (unlikely(set == 0)) break; /* use ep1/ep2 double-buffering for OUT */ if (!(size & PACKET_ACTIVE))
size = readl(®s->EPxSizeLB[ep->num]); if (!(size & PACKET_ACTIVE)) /* "can't happen" */ break;
size &= DATASIZE; /* EPxSizeH == 0 */
/* ep0out no-out-data case for set_config, etc */
} else
size = 0;
/* read all bytes from this packet */
req->req.actual += size;
is_short = (size < ep->ep.maxpacket); #ifdef USB_TRACE
VDBG(ep->dev, "read %s %u bytes%s OUT req %p %u/%u\n",
ep->ep.name, size, is_short ? "/S" : "",
req, req->req.actual, req->req.length); #endif while (likely(size-- != 0)) {
u8 byte = (u8) readl(ep->reg_fifo);
if (unlikely(bufferspace == 0)) { /* this happens when the driver's buffer * is smaller than what the host sent. * discard the extra data in this packet.
*/ if (req->req.status != -EOVERFLOW)
DBG(ep->dev, "%s overflow %u\n",
ep->ep.name, size);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
/* completion */ if (unlikely(is_short || req->req.actual == req->req.length)) { if (unlikely(ep->num == 0)) { /* non-control endpoints now usable? */ if (ep->dev->req_config)
writel(ep->dev->configured
? USBSTATE_CONFIGURED
: 0,
®s->UsbState); /* ep0out status stage */
writel(~(1<<0), ®s->EOP);
ep->stopped = 1;
ep->dev->ep0state = EP0_STATUS;
}
done(ep, req, 0);
/* empty the second buffer asap */ if (dbuff && !list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct goku_request, queue); goto top;
} return 1;
}
} while (dbuff); return 0;
}
staticinlinevoid
pio_irq_enable(struct goku_udc *dev, struct goku_udc_regs __iomem *regs, int epnum)
{
dev->int_enable |= INT_EPxDATASET (epnum);
writel(dev->int_enable, ®s->int_enable); /* write may still be posted */
}
staticinlinevoid
pio_irq_disable(struct goku_udc *dev, struct goku_udc_regs __iomem *regs, int epnum)
{
dev->int_enable &= ~INT_EPxDATASET (epnum);
writel(dev->int_enable, ®s->int_enable); /* write may still be posted */
}
/* re-init the bits affecting IN dma; careful with zlps */ if (likely(ep->is_in)) { if (unlikely(master & MST_RD_ENA)) {
DBG (ep->dev, "start, IN active dma %03x!!\n",
master); // return -EL2HLT;
}
writel(end, ®s->in_dma_end);
writel(start, ®s->in_dma_start);
/* Goku DMA-OUT merges short packets, which plays poorly with * protocols where short packets mark the transfer boundaries. * The chip supports a nonstandard policy with INT_MSTWRTMOUT, * ending transfers after 3 SOFs; we don't turn it on.
*/
} else { if (unlikely(master & MST_WR_ENA)) {
DBG (ep->dev, "start, OUT active dma %03x!!\n",
master); // return -EL2HLT;
}
writel(end, ®s->out_dma_end);
writel(start, ®s->out_dma_start);
/* normal hw dma completion (not abort) */ if (likely(ep->is_in)) { if (unlikely(master & MST_RD_ENA)) return;
req->req.actual = readl(®s->in_dma_current);
} else { if (unlikely(master & MST_WR_ENA)) return;
/* hardware merges short packets, and also hides packet * overruns. a partial packet MAY be in the fifo here.
*/
req->req.actual = readl(®s->out_dma_current);
}
req->req.actual -= req->req.dma;
req->req.actual++;
/* NAK future host requests, hoping the implicit delay lets the * dma engine finish reading (or writing) its latest packet and * empty the dma buffer (up to 16 bytes). * * This avoids needing to clean up a partial packet in the fifo; * we can't do that for IN without side effects to HALT and TOGGLE.
*/
command(regs, COMMAND_FIFO_DISABLE, ep->num);
req = list_entry(ep->queue.next, struct goku_request, queue);
master = readl(®s->dma_master) & MST_RW_BITS;
/* FIXME using these resets isn't usably documented. this may * not work unless it's followed by disabling the endpoint. * * FIXME the OUT reset path doesn't even behave consistently.
*/ if (ep->is_in) { if (unlikely((readl(®s->dma_master) & MST_RD_ENA) == 0)) goto finished;
curr = readl(®s->in_dma_current);
/* always require a cpu-view buffer so pio works */
req = container_of(_req, struct goku_request, req); if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue))) return -EINVAL;
ep = container_of(_ep, struct goku_ep, ep); if (unlikely(!_ep || (!ep->ep.desc && ep->num != 0))) return -EINVAL;
dev = ep->dev; if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN;
/* can't touch registers when suspended */ if (dev->ep0state == EP0_SUSPEND) return -EBUSY;
/* set up dma mapping in case the caller didn't */ if (ep->dma) {
status = usb_gadget_map_request(&dev->gadget, &req->req,
ep->is_in); if (status) return status;
}
/* for ep0 IN without premature status, zlp is required and * writing EOP starts the status stage (OUT).
*/ if (unlikely(ep->num == 0 && ep->is_in))
_req->zero = 1;
/* kickstart this i/o queue? */
status = 0; if (list_empty(&ep->queue) && likely(!ep->stopped)) { /* dma: done after dma completion IRQ (or error) * pio: done after last fifo operation
*/ if (ep->dma)
status = start_dma(ep, req); else
status = (ep->is_in ? write_fifo : read_fifo)(ep, req);
if (unlikely(status != 0)) { if (status > 0)
status = 0;
req = NULL;
}
} /* else pio or dma irq handler advances the queue. */
if (likely(req != NULL))
list_add_tail(&req->queue, &ep->queue);
/* Non-desirable behavior: FIFO_CLEAR also clears the * endpoint halt feature. For OUT, we _could_ just read * the bytes out (PIO, if !ep->dma); for in, no choice.
*/ if (size)
command(regs, COMMAND_FIFO_CLEAR, ep->num);
}
switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_INT: /* single buffering is enough */
ep = &dev->ep[3].ep; if (usb_gadget_ep_match_desc(g, ep, desc, ep_comp)) return ep; break; case USB_ENDPOINT_XFER_BULK: if (usb_endpoint_dir_in(desc)) { /* DMA may be available */
ep = &dev->ep[2].ep; if (usb_gadget_ep_match_desc(g, ep, desc, ep_comp)) return ep;
} break; default: /* nothing */ ;
}
staticconstchar *udc_ep_state(enum ep0state state)
{ switch (state) { case EP0_DISCONNECT: return"ep0_disconnect"; case EP0_IDLE: return"ep0_idle"; case EP0_IN: return"ep0_in"; case EP0_OUT: return"ep0_out"; case EP0_STATUS: return"ep0_status"; case EP0_STALL: return"ep0_stall"; case EP0_SUSPEND: return"ep0_suspend";
}
return"ep0_?";
}
staticconstchar *udc_ep_status(u32 status)
{ switch (status & EPxSTATUS_EP_MASK) { case EPxSTATUS_EP_READY: return"ready"; case EPxSTATUS_EP_DATAIN: return"packet"; case EPxSTATUS_EP_FULL: return"full"; case EPxSTATUS_EP_TX_ERR: /* host will retry */ return"tx_err"; case EPxSTATUS_EP_RX_ERR: return"rx_err"; case EPxSTATUS_EP_BUSY: /* ep0 only */ return"busy"; case EPxSTATUS_EP_STALL: return"stall"; case EPxSTATUS_EP_INVALID: /* these "can't happen" */ return"invalid";
}
/* deassert reset, leave USB D+ at hi-Z (no pullup) * don't let INT_PWRDETECT sequence begin
*/
udelay(250);
writel(PW_RESETB, ®s->power_detect);
readl(®s->int_enable);
}
/* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second
*/
/* when a driver is successfully registered, it will receive * control requests including set_configuration(), which enables * non-control requests. then usb traffic follows until a * disconnect is reported. then a host may connect again, or * the driver might get unbound.
*/ staticint goku_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver)
{ struct goku_udc *dev = to_goku_udc(g);
/* hook up the driver */
dev->driver = driver;
/* * then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect.
*/
udc_enable(dev);
/* NOTE: CLEAR_FEATURE is done in software so that we can * synchronize transfer restarts after bulk IN stalls. data * won't even enter the fifo until the halt is cleared.
*/ switch (ctrl.bRequest) { case USB_REQ_CLEAR_FEATURE: switch (ctrl.bRequestType) { case USB_RECIP_ENDPOINT:
tmp = le16_to_cpu(ctrl.wIndex) & 0x0f; /* active endpoint */ if (tmp > 3 ||
(!dev->ep[tmp].ep.desc && tmp != 0)) goto stall; if (ctrl.wIndex & cpu_to_le16(
USB_DIR_IN)) { if (!dev->ep[tmp].is_in) goto stall;
} else { if (dev->ep[tmp].is_in) goto stall;
} if (ctrl.wValue != cpu_to_le16(
USB_ENDPOINT_HALT)) goto stall; if (tmp)
goku_clear_halt(&dev->ep[tmp]);
succeed: /* start ep0out status stage */
writel(~(1<<0), ®s->EOP);
dev->ep[0].stopped = 1;
dev->ep0state = EP0_STATUS; return; case USB_RECIP_DEVICE: /* device remote wakeup: always clear */ if (ctrl.wValue != cpu_to_le16(1)) goto stall;
VDBG(dev, "clear dev remote wakeup\n"); goto succeed; case USB_RECIP_INTERFACE: goto stall; default: /* pass to gadget driver */ break;
} break; default: break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.