📄 s3c2410_udc22222222.c
字号:
len = read_fifo_crq(&crq);
if (len != sizeof(crq)) {
dprintk("setup begin: fifo READ ERROR"
" wanted %d bytes got %d. Stalling out...\n",
sizeof(crq), len);
set_ep0_ss;
return;
}
/* nuke (ep, -EPROTO);*/
/* cope with automagic for some standard requests. */
dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
== USB_TYPE_STANDARD;
dev->req_config = 0;
dev->req_pending = 1;
switch (crq.bRequest) {
/* hardware restricts gadget drivers here! */
case USB_REQ_SET_CONFIGURATION:
dprintk("USB_REQ_SET_CONFIGURATION ... \n");
if (crq.bRequestType == USB_RECIP_DEVICE) {
/* reflect hardware's automagic
* up to the gadget driver.
*/
config_change:
dev->req_config = 1;
clear_ep_state(dev);
/* if !has_cfr, there's no synch
* else use AREN (later) not SA|OPR
* USIR0_IR0 acts edge sensitive
*/
}
break;
/* ... and here, even more ... */
case USB_REQ_SET_INTERFACE:
dprintk("USB_REQ_SET_INTERFACE ... \n");
if (crq.bRequestType == USB_RECIP_INTERFACE) {
/* udc hardware is broken by design:
* - altsetting may only be zero;
* - hw resets all interfaces' eps;
* - ep reset doesn't include halt(?).
*/
goto config_change;
}
break;
/* hardware was supposed to hide this */
case USB_REQ_SET_ADDRESS:
dprintk("USB_REQ_SET_ADDRESS ... \n");
tmp = crq.wValue & 0x7F;
__raw_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
// usbd_info.address = address;
// usbctl_next_state_on_event( kEvAddress );
out_pkt_ack(1);
if (crq.bRequestType == USB_RECIP_DEVICE) {
// ep0start(dev, 0, "address");
return;
}
break;
}
if (crq.bRequestType & USB_DIR_IN)
dev->ep0state = EP0_IN_DATA_PHASE;
else
dev->ep0state = EP0_OUT_DATA_PHASE;
ret = dev->driver->setup(&dev->gadget, &crq);
if (ret < 0) {
/* hardware automagic preventing STALL... */
if (dev->req_config) {
/* hardware sometimes neglects to tell
* tell us about config change events,
* so later ones may fail...
*/
dprintk("config change %02x fail %d?\n",
crq.bRequest, ret);
return;
/* TODO experiment: if has_cfr,
* hardware didn't ACK; maybe we
* could actually STALL!
*/
}
// stall:
/* the watchdog timer helps deal with cases
* where udc seems to clear FST wrongly, and
* then NAKs instead of STALLing.
*/
// ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");
// start_watchdog(dev);
dev->ep0state = EP0_STALL;
/* deferred i/o == no response yet */
} else if (dev->req_pending) {
dprintk("dev->req_pending... what now?\n");
#if 0
if (likely(dev->ep0state == EP0_IN_DATA_PHASE
|| dev->req_std || crq.wLength))
ep0start(dev, 0, "defer");
else
ep0start(dev, UDCCS0_IPR, "defer/IPR");
#endif
}
/* expect at least one data or status stage irq */
return;
}
break;
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
dprintk("EP0_IN_DATA_PHASE ... what now?\n");
#if 0
if (udccs0 & UDCCS0_OPR) {
UDCCS0 = UDCCS0_OPR|UDCCS0_FTF;
if (req)
done(ep, req, 0);
ep0_idle(dev);
} else /* irq was IPR clearing */ {
if (req) {
/* this IN packet might finish the request */
(void) write_ep0_fifo(ep, req);
} /* else IN token before response was written */
}
#endif
break;
case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
dprintk("EP0_OUT_DATA_PHASE ... what now?\n");
#if 0
if (udccs0 & UDCCS0_OPR) {
if (req) {
/* this OUT packet might finish the request */
if (read_ep0_fifo(ep, req))
done(ep, req, 0);
/* else more OUT packets expected */
} /* else OUT token before read was issued */
} else /* irq was IPR clearing */ {
if (req)
done(ep, req, 0);
ep0_idle(dev);
}
#endif
break;
case EP0_END_XFER:
dprintk("EP0_END_XFER ... what now?\n");
#if 0
if (req)
done(ep, req, 0);
/* ack control-IN status (maybe in-zlp was skipped)
* also appears after some config change events.
*/
if (udccs0 & UDCCS0_OPR)
UDCCS0 = UDCCS0_OPR;
ep0_idle(dev);
#endif
break;
case EP0_STALL:
set_ep0_ss;
break;
}
// USIR0 = USIR0_IR0;
}
#if 0
static void handle_ep(struct s3c2410_ep *ep)
{
struct s3c2410_request *req;
int is_in = ep->bEndpointAddress & USB_DIR_IN;
int completed;
u32 udccs, tmp;
do {
completed = 0;
if (likely (!list_empty(&ep->queue)))
req = list_entry(ep->queue.next,
struct s3c2410_request, queue);
else
req = 0;
// TODO check FST handling
udccs = *ep->reg_udccs;
if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */
tmp = UDCCS_BI_TUR;
if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
tmp |= UDCCS_BI_SST;
tmp &= udccs;
if (likely (tmp))
*ep->reg_udccs = tmp;
if (req && likely ((udccs & UDCCS_BI_TFS) != 0))
completed = write_fifo(ep, req);
} else { /* irq from RPC (or for ISO, ROF) */
if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
tmp = UDCCS_BO_SST | UDCCS_BO_DME;
else
tmp = UDCCS_IO_ROF | UDCCS_IO_DME;
tmp &= udccs;
if (likely(tmp))
*ep->reg_udccs = tmp;
/* fifos can hold packets, ready for reading... */
if (likely(req)) {
completed = read_fifo(ep, req);
} else
pio_irq_disable (ep->bEndpointAddress);
}
ep->pio_irqs++;
} while (completed);
}
#endif
/*
* s3c2410_udc_irq - interrupt handler
*
* avoid delays in ep0 processing. the control handshaking isn't always
* under software control, and delays could cause usb protocol errors.
*/
static irqreturn_t
s3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r)
{
struct s3c2410_udc *dev = _dev;
// static int sb_debug_cnt = 1;
int handled;
dprintk("irq: %d ...\n", irq);
do {
// int saveIdx = __raw_readl(S3C2410_UDC_INDEX_REG);
int usb_status = __raw_readl(S3C2410_UDC_USB_INT_REG);
int usbd_status = __raw_readl(S3C2410_UDC_EP_INT_REG);
handled = 0;
dprintk("check usbs=0x%02x, usbds=0x%02x\n",
usb_status, usbd_status);
/* ReSeT Interrupt Request - USB reset */
if (usb_status & S3C2410_UDC_USBINT_RESET) {
dprintk("USB reset\n");
// if( usbctl_next_state_on_event(kEvReset) != kError )
/* clear interrupt */
__raw_writel(S3C2410_UDC_USBINT_RESET,
S3C2410_UDC_USB_INT_REG);
dev->gadget.speed = USB_SPEED_FULL;
// memset(&dev->stats, 0, sizeof dev->stats);
/* driver and endpoints are still reset */
// enable_disconnect_irq();
handled = 1;
}
/* RESume Interrupt Request */
if (usb_status & S3C2410_UDC_USBINT_RESUME) {
dprintk("USB resume\n");
/* clear interrupt */
__raw_writel(S3C2410_UDC_USBINT_RESUME,
S3C2410_UDC_USB_INT_REG);
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume
/* && is_usb_connected() */)
dev->driver->resume(&dev->gadget);
handled = 1;
}
/* SUSpend Interrupt Request */
if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
dprintk("USB suspend\n");
/* clear interrupt */
__raw_writel(S3C2410_UDC_USBINT_SUSPEND,
S3C2410_UDC_USB_INT_REG);
// 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);
ep0_idle (dev);
handled = 1;
}
/* EP interrupts */
if (usbd_status) {
int i;
/* control traffic */
if (usbd_status & S3C2410_UDC_INT_EP0) {
// dev->ep[0].pio_irqs++;
dprintk("USB ep0 irq\n");
handle_ep0(dev);
// ep0_int_hndlr();
/* Clear the interrupt bit by setting it to 1 */
__raw_writel(S3C2410_UDC_INT_EP0,
S3C2410_UDC_EP_INT_REG);
handled = 1;
}
/* endpoint data transfers */
for (i = 1; i < 5; i++) {
u32 tmp = 1 << i;
if (usbd_status & tmp) {
dprintk("USB ep%d irq\n", i);
// handle_ep(&dev->ep[i]);
/* Clear the interrupt bit by setting it to 1 */
__raw_writel(tmp, S3C2410_UDC_EP_INT_REG);
handled = 1;
}
}
}
// UD_INDEX= saveIdx; /* restore idx */
} while (handled);
dprintk("irq: %d done.\n\n", irq);
return IRQ_HANDLED;
}
/*
* probe - binds to the platform device
*/
static int __init s3c2410_udc_probe(struct device *_dev)
{
struct s3c2410_udc *udc = &memory;
int retval; // , out_dma = 0;
dprintk("s3c2410_udc_probe\n");
/* other non-static parts of init */
// udc->dev = _dev;
// udc->mach = _dev->platform_data;
/*
init_timer(&dev->timer);
dev->timer.function = udc_watchdog;
dev->timer.data = (unsigned long) dev;
*/
device_initialize(&udc->gadget.dev);
udc->gadget.dev.parent = _dev;
udc->gadget.dev.dma_mask = _dev->dma_mask;
the_controller = udc;
dev_set_drvdata(_dev, udc);
// udc_disable(udc);
// udc_reinit(udc);
/* irq setup after old hardware state is cleaned up */
retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
SA_INTERRUPT, gadget_name, udc);
if (retval != 0) {
printk(KERN_ERR "%s: can't get irq %i, err %d\n",
gadget_name, IRQ_USBD, retval);
return -EBUSY;
}
dprintk("%s: got irq %i\n", gadget_name, IRQ_USBD);
udc->got_irq = 1;
// create_proc_files();
return 0;
}
static int __exit s3c2410_udc_remove(struct device *_dev)
{
struct s3c2410_udc *udc = _dev->driver_data;
dprintk("s3c2410_udc_remove\n");
// udc_disable(udc);
// remove_proc_files();
usb_gadget_unregister_driver(udc->driver);
if (udc->got_irq) {
free_irq(IRQ_USBD, udc);
udc->got_irq = 0;
}
dev_set_drvdata(_dev, 0);
the_controller = 0;
return 0;
}
/*-------------------------------------------------------------------------*/
static struct device_driver udc_driver = {
.name = "s3c2410-udc",
.bus = &platform_bus_type,
.probe = s3c2410_udc_probe,
.remove = __exit_p(s3c2410_udc_remove),
// FIXME power management support
// .suspend = ... disable UDC
// .resume = ... re-enable UDC
};
static int __init udc_init(void)
{
int tmp;
dprintk("%s: version %s\n", gadget_name, DRIVER_VERSION);
tmp = __raw_readl(S3C2410_CLKCON);
tmp &= ~S3C2410_CLKCON_USBD;
__raw_writel(tmp, S3C2410_CLKCON);
tmp = __raw_readl(S3C2410_MISCCR);
tmp &= ~S3C2410_MISCCR_USBHOST;
__raw_writel(tmp, S3C2410_MISCCR);
/* UPLLCON */
tmp = (0x78 << S3C2410_PLLCON_MDIVSHIFT)
| (0x02 << S3C2410_PLLCON_PDIVSHIFT)
| (0x03 << S3C2410_PLLCON_SDIVSHIFT);
__raw_writel(tmp, S3C2410_UPLLCON);
tmp = __raw_readl(S3C2410_CLKCON);
tmp |= S3C2410_CLKCON_USBD;
__raw_writel(tmp, S3C2410_CLKCON);
mdelay(10);
return driver_register(&udc_driver);
}
module_init(udc_init);
static void __exit udc_exit(void)
{
driver_unregister(&udc_driver);
}
module_exit(udc_exit);
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("Herbert P鰐zl");
MODULE_LICENSE ("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -