📄 lh7a40x_udc.c
字号:
}
return 0;
}
/** Read to request from FIFO (max read == bytes in fifo)
* Return: 0 = still running, 1 = completed, negative = errno
* NOTE: INDEX register must be set for EP
*/
static int read_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 csr;
u8 *buf;
unsigned bufferspace, count, is_short;
volatile u32 *fifo = (volatile u32 *)ep->fifo;
/* make sure there's a packet in the FIFO. */
csr = usb_read(ep->csr1);
if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) {
DEBUG("%s: Packet NOT ready!\n", __FUNCTION__);
return -EINVAL;
}
buf = req->req.buf + req->req.actual;
prefetchw(buf);
bufferspace = req->req.length - req->req.actual;
/* read all bytes from this packet */
count = usb_read(USB_OUT_FIFO_WC1);
req->req.actual += min(count, bufferspace);
is_short = (count < ep->ep.maxpacket);
DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n",
ep->ep.name, csr, count,
is_short ? "/S" : "", req, req->req.actual, req->req.length);
while (likely(count-- != 0)) {
u8 byte = (u8) (*fifo & 0xff);
if (unlikely(bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data.
*/
if (req->req.status != -EOVERFLOW)
printk("%s overflow %d\n", ep->ep.name, count);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1);
/* completion */
if (is_short || req->req.actual == req->req.length) {
done(ep, req, 0);
usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1);
if (list_empty(&ep->queue))
pio_irq_disable(ep_index(ep));
return 1;
}
/* finished that packet. the next one may be waiting... */
return 0;
}
/*
* done - retire a request; caller blocked irqs
* INDEX register is preserved to keep same
*/
static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status)
{
unsigned int stopped = ep->stopped;
u32 index;
DEBUG("%s, %p\n", __FUNCTION__, ep);
list_del_init(&req->queue);
if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
if (status && status != -ESHUTDOWN)
DEBUG("complete %s req %p stat %d len %u/%u\n",
ep->ep.name, &req->req, status,
req->req.actual, req->req.length);
/* don't modify queue heads during completion callback */
ep->stopped = 1;
/* Read current index (completion may modify it) */
index = usb_read(USB_INDEX);
spin_unlock(&ep->dev->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&ep->dev->lock);
/* Restore index */
usb_set_index(index);
ep->stopped = stopped;
}
/** Enable EP interrupt */
static void pio_irq_enable(int ep)
{
DEBUG("%s: %d\n", __FUNCTION__, ep);
switch (ep) {
case 1:
usb_set(USB_IN_INT_EP1, USB_IN_INT_EN);
break;
case 2:
usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN);
break;
case 3:
usb_set(USB_IN_INT_EP3, USB_IN_INT_EN);
break;
default:
DEBUG("Unknown endpoint: %d\n", ep);
break;
}
}
/** Disable EP interrupt */
static void pio_irq_disable(int ep)
{
DEBUG("%s: %d\n", __FUNCTION__, ep);
switch (ep) {
case 1:
usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN);
break;
case 2:
usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN);
break;
case 3:
usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN);
break;
default:
DEBUG("Unknown endpoint: %d\n", ep);
break;
}
}
/*
* nuke - dequeue ALL requests
*/
void nuke(struct lh7a40x_ep *ep, int status)
{
struct lh7a40x_request *req;
DEBUG("%s, %p\n", __FUNCTION__, ep);
/* Flush FIFO */
flush(ep);
/* called with irqs blocked */
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
done(ep, req, status);
}
/* Disable IRQ if EP is enabled (has descriptor) */
if (ep->desc)
pio_irq_disable(ep_index(ep));
}
/*
void nuke_all(struct lh7a40x_udc *dev)
{
int n;
for(n=0; n<UDC_MAX_ENDPOINTS; n++) {
struct lh7a40x_ep *ep = &dev->ep[n];
usb_set_index(n);
nuke(ep, 0);
}
}*/
/*
static void flush_all(struct lh7a40x_udc *dev)
{
int n;
for (n = 0; n < UDC_MAX_ENDPOINTS; n++)
{
struct lh7a40x_ep *ep = &dev->ep[n];
flush(ep);
}
}
*/
/** Flush EP
* NOTE: INDEX register must be set before this call
*/
static void flush(struct lh7a40x_ep *ep)
{
DEBUG("%s, %p\n", __FUNCTION__, ep);
switch (ep->ep_type) {
case ep_control:
/* check, by implication c.f. 15.1.2.11 */
break;
case ep_bulk_in:
case ep_interrupt:
/* if(csr & USB_IN_CSR1_IN_PKT_RDY) */
usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1);
break;
case ep_bulk_out:
/* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */
usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1);
break;
}
}
/**
* lh7a40x_in_epn - handle IN interrupt
*/
static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr)
{
u32 csr;
struct lh7a40x_ep *ep = &dev->ep[ep_idx];
struct lh7a40x_request *req;
usb_set_index(ep_idx);
csr = usb_read(ep->csr1);
DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr);
if (csr & USB_IN_CSR1_SENT_STALL) {
DEBUG("USB_IN_CSR1_SENT_STALL\n");
usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ ,
ep->csr1);
return;
}
if (!ep->desc) {
DEBUG("%s: NO EP DESC\n", __FUNCTION__);
return;
}
if (list_empty(&ep->queue))
req = 0;
else
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
DEBUG("req: %p\n", req);
if (!req)
return;
write_fifo(ep, req);
}
/* ********************************************************************************************* */
/* Bulk OUT (recv)
*/
static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr)
{
struct lh7a40x_ep *ep = &dev->ep[ep_idx];
struct lh7a40x_request *req;
DEBUG("%s: %d\n", __FUNCTION__, ep_idx);
usb_set_index(ep_idx);
if (ep->desc) {
u32 csr;
csr = usb_read(ep->csr1);
while ((csr =
usb_read(ep->
csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY |
USB_OUT_CSR1_SENT_STALL)) {
DEBUG("%s: %x\n", __FUNCTION__, csr);
if (csr & USB_OUT_CSR1_SENT_STALL) {
DEBUG("%s: stall sent, flush fifo\n",
__FUNCTION__);
/* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */
flush(ep);
} else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) {
if (list_empty(&ep->queue))
req = 0;
else
req =
list_entry(ep->queue.next,
struct lh7a40x_request,
queue);
if (!req) {
printk("%s: NULL REQ %d\n",
__FUNCTION__, ep_idx);
flush(ep);
break;
} else {
read_fifo(ep, req);
}
}
}
} else {
/* Throw packet away.. */
printk("%s: No descriptor?!?\n", __FUNCTION__);
flush(ep);
}
}
static void stop_activity(struct lh7a40x_udc *dev,
struct usb_gadget_driver *driver)
{
int i;
/* don't disconnect drivers more than once */
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = 0;
dev->gadget.speed = USB_SPEED_UNKNOWN;
/* prevent new request submissions, kill any outstanding requests */
for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {
struct lh7a40x_ep *ep = &dev->ep[i];
ep->stopped = 1;
usb_set_index(i);
nuke(ep, -ESHUTDOWN);
}
/* report disconnect; the driver is already quiesced */
if (driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
}
/* re-init driver-visible data structures */
udc_reinit(dev);
}
/** Handle USB RESET interrupt
*/
static void lh7a40x_reset_intr(struct lh7a40x_udc *dev)
{
#if 0 /* def CONFIG_ARCH_LH7A404 */
/* Does not work always... */
DEBUG("%s: %d\n", __FUNCTION__, dev->usb_address);
if (!dev->usb_address) {
/*usb_set(USB_RESET_IO, USB_RESET);
mdelay(5);
usb_clear(USB_RESET_IO, USB_RESET); */
return;
}
/* Put the USB controller into reset. */
usb_set(USB_RESET_IO, USB_RESET);
/* Set Device ID to 0 */
udc_set_address(dev, 0);
/* Let PLL2 settle down */
mdelay(5);
/* Release the USB controller from reset */
usb_clear(USB_RESET_IO, USB_RESET);
/* Re-enable UDC */
udc_enable(dev);
#endif
dev->gadget.speed = USB_SPEED_FULL;
}
/*
* lh7a40x usb client interrupt handler.
*/
static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev, struct pt_regs *r)
{
struct lh7a40x_udc *dev = _dev;
DEBUG("\n\n");
spin_lock(&dev->lock);
for (;;) {
u32 intr_in = usb_read(USB_IN_INT);
u32 intr_out = usb_read(USB_OUT_INT);
u32 intr_int = usb_read(USB_INT);
/* Test also against enable bits.. (lh7a40x errata).. Sigh.. */
u32 in_en = usb_read(USB_IN_INT_EN);
u32 out_en = usb_read(USB_OUT_INT_EN);
if (!intr_out && !intr_in && !intr_int)
break;
DEBUG("%s (on state %s)\n", __FUNCTION__,
state_names[dev->ep0state]);
DEBUG("intr_out = %x\n", intr_out);
DEBUG("intr_in = %x\n", intr_in);
DEBUG("intr_int = %x\n", intr_int);
if (intr_in) {
usb_write(intr_in, USB_IN_INT);
if ((intr_in & USB_IN_INT_EP1)
&& (in_en & USB_IN_INT_EP1)) {
DEBUG("USB_IN_INT_EP1\n");
lh7a40x_in_epn(dev, 1, intr_in);
}
if ((intr_in & USB_IN_INT_EP3)
&& (in_en & USB_IN_INT_EP3)) {
DEBUG("USB_IN_INT_EP3\n");
lh7a40x_in_epn(dev, 3, intr_in);
}
if (intr_in & USB_IN_INT_EP0) {
DEBUG("USB_IN_INT_EP0 (control)\n");
lh7a40x_handle_ep0(dev, intr_in);
}
}
if (intr_out) {
usb_write(intr_out, USB_OUT_INT);
if ((intr_out & USB_OUT_INT_EP2)
&& (out_en & USB_OUT_INT_EP2)) {
DEBUG("USB_OUT_INT_EP2\n");
lh7a40x_out_epn(dev, 2, intr_out);
}
}
if (intr_int) {
usb_write(intr_int, USB_INT);
if (intr_int & USB_INT_RESET_INT) {
lh7a40x_reset_intr(dev);
}
if (intr_int & USB_INT_RESUME_INT) {
DEBUG("USB resume\n");
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume
&& is_usb_connected()) {
dev->driver->resume(&dev->gadget);
}
}
if (intr_int & USB_INT_SUSPEND_INT) {
DEBUG("USB suspend%s\n",
is_usb_connected()? "" : "+disconnect");
if (!is_usb_connected()) {
stop_activity(dev, dev->driver);
} else if (dev->gadget.speed !=
USB_SPEED_UNKNOWN && dev->driver
&& dev->driver->suspend) {
dev->driver->suspend(&dev->gadget);
}
}
}
}
spin_unlock(&dev->lock);
return IRQ_HANDLED;
}
static int lh7a40x_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct lh7a40x_ep *ep;
struct lh7a40x_udc *dev;
unsigned long flags;
DEBUG("%s, %p\n", __FUNCTION__, _ep);
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
|| ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
DEBUG("%s, bad ep or descriptor\n", __FUNCTION__);
return -EINVAL;
}
/* xfer types must match, except that interrupt ~= bulk */
if (ep->bmAttributes != desc->bmAttributes
&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK
&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name);
return -EINVAL;
}
/* hardware _could_ do smaller, but driver doesn't */
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))
|| !desc->wMaxPacketSize) {
DEBUG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
return -ERANGE;
}
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
DEBUG("%s, bogus device state\n", __FUNCTION__);
return -ESHUTDOWN;
}
spin_lock_irqsave(&ep->dev->lock, flags);
ep->stopped = 0;
ep->desc = desc;
ep->pio_irqs = 0;
ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
/* Reset halt state (does flush) */
lh7a40x_set_halt(_ep, 0);
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name);
return 0;
}
/** Disable EP
* NOTE: Sets INDEX register
*/
static int lh7a40x_ep_disable(struct usb_ep *_ep)
{
struct lh7a40x_ep *ep;
unsigned long flags;
DEBUG("%s, %p\n", __FUNCTION__, _ep);
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep || !ep->desc) {
DEBUG("%s, %s not enabled\n", __FUNCTION__,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -