📄 s3c2410_udc.c
字号:
.pullup = s3c2410_udc_pullup,
.vbus_session = s3c2410_udc_vbus_session,
.vbus_draw = s3c2410_vbus_draw,
};
/*------------------------- gadget driver handling---------------------------*/
/*
* s3c2410_udc_disable
*/
static void s3c2410_udc_disable(struct s3c2410_udc *dev)
{
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
/* Disable all interrupts */
udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG);
udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG);
/* Clear the interrupt registers */
udc_write(S3C2410_UDC_USBINT_RESET
| S3C2410_UDC_USBINT_RESUME
| S3C2410_UDC_USBINT_SUSPEND,
S3C2410_UDC_USB_INT_REG);
udc_write(0x1F, S3C2410_UDC_EP_INT_REG);
/* Good bye, cruel world */
if (udc_info && udc_info->udc_command)
udc_info->udc_command(S3C2410_UDC_P_DISABLE);
/* Set speed to unknown */
dev->gadget.speed = USB_SPEED_UNKNOWN;
}
/*
* s3c2410_udc_reinit
*/
static void s3c2410_udc_reinit(struct s3c2410_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;
for (i = 0; i < S3C2410_ENDPOINTS; i++) {
struct s3c2410_ep *ep = &dev->ep[i];
if (i != 0)
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
ep->dev = dev;
ep->desc = NULL;
ep->halted = 0;
INIT_LIST_HEAD (&ep->queue);
}
}
/*
* s3c2410_udc_enable
*/
static void s3c2410_udc_enable(struct s3c2410_udc *dev)
{
int i;
dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n");
/* dev->gadget.speed = USB_SPEED_UNKNOWN; */
dev->gadget.speed = USB_SPEED_FULL;
/* Set MAXP for all endpoints */
for (i = 0; i < S3C2410_ENDPOINTS; i++) {
udc_write(i, S3C2410_UDC_INDEX_REG);
udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3,
S3C2410_UDC_MAXP_REG);
}
/* Set default power state */
udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
/* Enable reset and suspend interrupt interrupts */
udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND,
S3C2410_UDC_USB_INT_EN_REG);
/* Enable ep0 interrupt */
udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
/* time to say "hello, world" */
if (udc_info && udc_info->udc_command)
udc_info->udc_command(S3C2410_UDC_P_ENABLE);
}
/*
* usb_gadget_register_driver
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct s3c2410_udc *udc = the_controller;
int retval;
dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
driver->driver.name);
/* Sanity checks */
if (!udc)
return -ENODEV;
if (udc->driver)
return -EBUSY;
if (!driver->bind || !driver->setup
|| driver->speed != USB_SPEED_FULL) {
printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
driver->bind, driver->setup, driver->speed);
return -EINVAL;
}
#if defined(MODULE)
if (!driver->unbind) {
printk(KERN_ERR "Invalid driver: no unbind method\n");
return -EINVAL;
}
#endif
/* Hook the driver */
udc->driver = driver;
udc->gadget.dev.driver = &driver->driver;
/* Bind the driver */
if ((retval = device_add(&udc->gadget.dev)) != 0) {
printk(KERN_ERR "Error in device_add() : %d\n",retval);
goto register_error;
}
dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n",
driver->driver.name);
if ((retval = driver->bind (&udc->gadget)) != 0) {
device_del(&udc->gadget.dev);
goto register_error;
}
/* Enable udc */
s3c2410_udc_enable(udc);
return 0;
register_error:
udc->driver = NULL;
udc->gadget.dev.driver = NULL;
return retval;
}
/*
* usb_gadget_unregister_driver
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct s3c2410_udc *udc = the_controller;
if (!udc)
return -ENODEV;
if (!driver || driver != udc->driver || !driver->unbind)
return -EINVAL;
dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
driver->driver.name);
if (driver->disconnect)
driver->disconnect(&udc->gadget);
device_del(&udc->gadget.dev);
udc->driver = NULL;
/* Disable udc */
s3c2410_udc_disable(udc);
return 0;
}
/*---------------------------------------------------------------------------*/
static struct s3c2410_udc memory = {
.gadget = {
.ops = &s3c2410_ops,
.ep0 = &memory.ep[0].ep,
.name = gadget_name,
.dev = {
.bus_id = "gadget",
},
},
/* control endpoint */
.ep[0] = {
.num = 0,
.ep = {
.name = ep0name,
.ops = &s3c2410_ep_ops,
.maxpacket = EP0_FIFO_SIZE,
},
.dev = &memory,
},
/* first group of endpoints */
.ep[1] = {
.num = 1,
.ep = {
.name = "ep1-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[2] = {
.num = 2,
.ep = {
.name = "ep2-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[3] = {
.num = 3,
.ep = {
.name = "ep3-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 3,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[4] = {
.num = 4,
.ep = {
.name = "ep4-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 4,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
}
};
/*
* probe - binds to the platform device
*/
static int s3c2410_udc_probe(struct platform_device *pdev)
{
struct s3c2410_udc *udc = &memory;
struct device *dev = &pdev->dev;
int retval;
unsigned int irq;
dev_dbg(dev, "%s()\n", __func__);
usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
if (IS_ERR(usb_bus_clock)) {
dev_err(dev, "failed to get usb bus clock source\n");
return PTR_ERR(usb_bus_clock);
}
clk_enable(usb_bus_clock);
udc_clock = clk_get(NULL, "usb-device");
if (IS_ERR(udc_clock)) {
dev_err(dev, "failed to get udc clock source\n");
return PTR_ERR(udc_clock);
}
clk_enable(udc_clock);
mdelay(10);
dev_dbg(dev, "got and enabled clocks\n");
if (strncmp(pdev->name, "s3c2440", 7) == 0) {
dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
}
spin_lock_init (&udc->lock);
udc_info = pdev->dev.platform_data;
rsrc_start = S3C2410_PA_USBDEV;
rsrc_len = S3C24XX_SZ_USBDEV;
if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
return -EBUSY;
base_addr = ioremap(rsrc_start, rsrc_len);
if (!base_addr) {
retval = -ENOMEM;
goto err_mem;
}
device_initialize(&udc->gadget.dev);
udc->gadget.dev.parent = &pdev->dev;
udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
the_controller = udc;
platform_set_drvdata(pdev, udc);
s3c2410_udc_disable(udc);
s3c2410_udc_reinit(udc);
/* irq setup after old hardware state is cleaned up */
retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
IRQF_DISABLED, gadget_name, udc);
if (retval != 0) {
dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
retval = -EBUSY;
goto err_map;
}
dev_dbg(dev, "got irq %i\n", IRQ_USBD);
if (udc_info && udc_info->vbus_pin > 0) {
irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
retval = request_irq(irq, s3c2410_udc_vbus_irq,
IRQF_DISABLED | IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING,
gadget_name, udc);
if (retval != 0) {
dev_err(dev, "can't get vbus irq %i, err %d\n",
irq, retval);
retval = -EBUSY;
goto err_int;
}
dev_dbg(dev, "got irq %i\n", irq);
} else {
udc->vbus = 1;
}
if (s3c2410_udc_debugfs_root) {
udc->regs_info = debugfs_create_file("registers", S_IRUGO,
s3c2410_udc_debugfs_root,
udc, &s3c2410_udc_debugfs_fops);
if (IS_ERR(udc->regs_info)) {
dev_warn(dev, "debugfs file creation failed %ld\n",
PTR_ERR(udc->regs_info));
udc->regs_info = NULL;
}
}
dev_dbg(dev, "probe ok\n");
return 0;
err_int:
free_irq(IRQ_USBD, udc);
err_map:
iounmap(base_addr);
err_mem:
release_mem_region(rsrc_start, rsrc_len);
return retval;
}
/*
* s3c2410_udc_remove
*/
static int s3c2410_udc_remove(struct platform_device *pdev)
{
struct s3c2410_udc *udc = platform_get_drvdata(pdev);
unsigned int irq;
dev_dbg(&pdev->dev, "%s()\n", __func__);
if (udc->driver)
return -EBUSY;
debugfs_remove(udc->regs_info);
if (udc_info && udc_info->vbus_pin > 0) {
irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
free_irq(irq, udc);
}
free_irq(IRQ_USBD, udc);
iounmap(base_addr);
release_mem_region(rsrc_start, rsrc_len);
platform_set_drvdata(pdev, NULL);
if (!IS_ERR(udc_clock) && udc_clock != NULL) {
clk_disable(udc_clock);
clk_put(udc_clock);
udc_clock = NULL;
}
if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
clk_disable(usb_bus_clock);
clk_put(usb_bus_clock);
usb_bus_clock = NULL;
}
dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
return 0;
}
#ifdef CONFIG_PM
static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
{
if (udc_info && udc_info->udc_command)
udc_info->udc_command(S3C2410_UDC_P_DISABLE);
return 0;
}
static int s3c2410_udc_resume(struct platform_device *pdev)
{
if (udc_info && udc_info->udc_command)
udc_info->udc_command(S3C2410_UDC_P_ENABLE);
return 0;
}
#else
#define s3c2410_udc_suspend NULL
#define s3c2410_udc_resume NULL
#endif
static struct platform_driver udc_driver_2410 = {
.driver = {
.name = "s3c2410-usbgadget",
.owner = THIS_MODULE,
},
.probe = s3c2410_udc_probe,
.remove = s3c2410_udc_remove,
.suspend = s3c2410_udc_suspend,
.resume = s3c2410_udc_resume,
};
static struct platform_driver udc_driver_2440 = {
.driver = {
.name = "s3c2440-usbgadget",
.owner = THIS_MODULE,
},
.probe = s3c2410_udc_probe,
.remove = s3c2410_udc_remove,
.suspend = s3c2410_udc_suspend,
.resume = s3c2410_udc_resume,
};
static int __init udc_init(void)
{
int retval;
dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
if (IS_ERR(s3c2410_udc_debugfs_root)) {
printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",
gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
s3c2410_udc_debugfs_root = NULL;
}
retval = platform_driver_register(&udc_driver_2410);
if (retval)
goto err;
retval = platform_driver_register(&udc_driver_2440);
if (retval)
goto err;
return 0;
err:
debugfs_remove(s3c2410_udc_debugfs_root);
return retval;
}
static void __exit udc_exit(void)
{
platform_driver_unregister(&udc_driver_2410);
platform_driver_unregister(&udc_driver_2440);
debugfs_remove(s3c2410_udc_debugfs_root);
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
EXPORT_SYMBOL(usb_gadget_register_driver);
module_init(udc_init);
module_exit(udc_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -