📄 lh7a40x_udc.c
字号:
/* * linux/drivers/usb/gadget/lh7a40x_udc.c * Sharp LH7A40x on-chip full speed USB device controllers * * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID * Copyright (C) 2004 Bo Henriksen, Nordic ID * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/platform_device.h>#include "lh7a40x_udc.h"//#define DEBUG printk//#define DEBUG_EP0 printk//#define DEBUG_SETUP printk#ifndef DEBUG_EP0# define DEBUG_EP0(fmt,args...)#endif#ifndef DEBUG_SETUP# define DEBUG_SETUP(fmt,args...)#endif#ifndef DEBUG# define NO_STATES# define DEBUG(fmt,args...)#endif#define DRIVER_DESC "LH7A40x USB Device Controller"#define DRIVER_VERSION __DATE__#ifndef _BIT /* FIXME - what happended to _BIT in 2.6.7bk18? */#define _BIT(x) (1<<(x))#endifstruct lh7a40x_udc *the_controller;static const char driver_name[] = "lh7a40x_udc";static const char driver_desc[] = DRIVER_DESC;static const char ep0name[] = "ep0-control";/* Local definintions.*/#ifndef NO_STATESstatic char *state_names[] = { "WAIT_FOR_SETUP", "DATA_STATE_XMIT", "DATA_STATE_NEED_ZLP", "WAIT_FOR_OUT_STATUS", "DATA_STATE_RECV"};#endif/* Local declarations.*/static int lh7a40x_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *);static int lh7a40x_ep_disable(struct usb_ep *ep);static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t);static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *);static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *, gfp_t);static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t, unsigned);static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t);static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *);static int lh7a40x_set_halt(struct usb_ep *ep, int);static int lh7a40x_fifo_status(struct usb_ep *ep);static int lh7a40x_fifo_status(struct usb_ep *ep);static void lh7a40x_fifo_flush(struct usb_ep *ep);static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep);static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr);static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status);static void pio_irq_enable(int bEndpointAddress);static void pio_irq_disable(int bEndpointAddress);static void stop_activity(struct lh7a40x_udc *dev, struct usb_gadget_driver *driver);static void flush(struct lh7a40x_ep *ep);static void udc_enable(struct lh7a40x_udc *dev);static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address);static struct usb_ep_ops lh7a40x_ep_ops = { .enable = lh7a40x_ep_enable, .disable = lh7a40x_ep_disable, .alloc_request = lh7a40x_alloc_request, .free_request = lh7a40x_free_request, .alloc_buffer = lh7a40x_alloc_buffer, .free_buffer = lh7a40x_free_buffer, .queue = lh7a40x_queue, .dequeue = lh7a40x_dequeue, .set_halt = lh7a40x_set_halt, .fifo_status = lh7a40x_fifo_status, .fifo_flush = lh7a40x_fifo_flush,};/* Inline code */static __inline__ int write_packet(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int max){ u8 *buf; int length, count; volatile u32 *fifo = (volatile u32 *)ep->fifo; buf = req->req.buf + req->req.actual; prefetch(buf); length = req->req.length - req->req.actual; length = min(length, max); req->req.actual += length; DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); count = length; while (count--) { *fifo = *buf++; } return length;}static __inline__ void usb_set_index(u32 ep){ *(volatile u32 *)io_p2v(USB_INDEX) = ep;}static __inline__ u32 usb_read(u32 port){ return *(volatile u32 *)io_p2v(port);}static __inline__ void usb_write(u32 val, u32 port){ *(volatile u32 *)io_p2v(port) = val;}static __inline__ void usb_set(u32 val, u32 port){ volatile u32 *ioport = (volatile u32 *)io_p2v(port); u32 after = (*ioport) | val; *ioport = after;}static __inline__ void usb_clear(u32 val, u32 port){ volatile u32 *ioport = (volatile u32 *)io_p2v(port); u32 after = (*ioport) & ~val; *ioport = after;}/*-------------------------------------------------------------------------*/#define GPIO_PORTC_DR (0x80000E08)#define GPIO_PORTC_DDR (0x80000E18)#define GPIO_PORTC_PDR (0x80000E70)/* get port C pin data register */#define get_portc_pdr(bit) ((usb_read(GPIO_PORTC_PDR) & _BIT(bit)) != 0)/* get port C data direction register */#define get_portc_ddr(bit) ((usb_read(GPIO_PORTC_DDR) & _BIT(bit)) != 0)/* set port C data register */#define set_portc_dr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DR) : usb_clear(_BIT(bit), GPIO_PORTC_DR))/* set port C data direction register */#define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR))/* * LPD7A404 GPIO's: * Port C bit 1 = USB Port 1 Power Enable * Port C bit 2 = USB Port 1 Data Carrier Detect */#define is_usb_connected() get_portc_pdr(2)#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 lh7a40x_udc *dev = _dev; char *next = buf; unsigned size = count; unsigned long flags; int t; if (off != 0) return 0; local_irq_save(flags); /* basic device status */ t = scnprintf(next, size, DRIVER_DESC "\n" "%s version: %s\n" "Gadget driver: %s\n" "Host: %s\n\n", driver_name, DRIVER_VERSION, dev->driver ? dev->driver->driver.name : "(none)", is_usb_connected()? "full speed" : "disconnected"); size -= t; next += t; t = scnprintf(next, size, "GPIO:\n" " Port C bit 1: %d, dir %d\n" " Port C bit 2: %d, dir %d\n\n", get_portc_pdr(1), get_portc_ddr(1), get_portc_pdr(2), get_portc_ddr(2) ); size -= t; next += t; t = scnprintf(next, size, "DCP pullup: %d\n\n", (usb_read(USB_PM) & PM_USB_DCP) != 0); size -= t; next += t; 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 *//* * udc_disable - disable USB device controller */static void udc_disable(struct lh7a40x_udc *dev){ DEBUG("%s, %p\n", __FUNCTION__, dev); udc_set_address(dev, 0); /* Disable interrupts */ usb_write(0, USB_IN_INT_EN); usb_write(0, USB_OUT_INT_EN); usb_write(0, USB_INT_EN); /* Disable the USB */ usb_write(0, USB_PM);#ifdef CONFIG_ARCH_LH7A404 /* Disable USB power */ set_portc_dr(1, 0);#endif /* if hardware supports it, disconnect from usb */ /* make_usb_disappear(); */ dev->ep0state = WAIT_FOR_SETUP; dev->gadget.speed = USB_SPEED_UNKNOWN; dev->usb_address = 0;}/* * udc_reinit - initialize software state */static void udc_reinit(struct lh7a40x_udc *dev){ u32 i; DEBUG("%s, %p\n", __FUNCTION__, dev); /* device/ep0 records init */ INIT_LIST_HEAD(&dev->gadget.ep_list); INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); dev->ep0state = WAIT_FOR_SETUP; /* basic endpoint records init */ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { struct lh7a40x_ep *ep = &dev->ep[i]; if (i != 0) list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ep->desc = 0; ep->stopped = 0; INIT_LIST_HEAD(&ep->queue); ep->pio_irqs = 0; } /* the rest was statically initialized, and is read-only */}#define BYTES2MAXP(x) (x / 8)#define MAXP2BYTES(x) (x * 8)/* until it's enabled, this UDC should be completely invisible * to any USB host. */static void udc_enable(struct lh7a40x_udc *dev){ int ep; DEBUG("%s, %p\n", __FUNCTION__, dev); dev->gadget.speed = USB_SPEED_UNKNOWN;#ifdef CONFIG_ARCH_LH7A404 /* Set Port C bit 1 & 2 as output */ set_portc_ddr(1, 1); set_portc_ddr(2, 1); /* Enable USB power */ set_portc_dr(1, 0);#endif /* * C.f Chapter 18.1.3.1 Initializing the USB */ /* Disable the USB */ usb_clear(PM_USB_ENABLE, USB_PM); /* Reset APB & I/O sides of the USB */ usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET); mdelay(5); usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET); /* Set MAXP values for each */ for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { struct lh7a40x_ep *ep_reg = &dev->ep[ep]; u32 csr; usb_set_index(ep); switch (ep_reg->ep_type) { case ep_bulk_in: case ep_interrupt: usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, ep_reg->csr2); /* Fall through */ case ep_control: usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), USB_IN_MAXP); break; case ep_bulk_out: usb_clear(USB_OUT_CSR2_USB_DMA_EN | USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2); usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), USB_OUT_MAXP); break; } /* Read & Write CSR1, just in case */ csr = usb_read(ep_reg->csr1); usb_write(csr, ep_reg->csr1); flush(ep_reg); } /* Disable interrupts */ usb_write(0, USB_IN_INT_EN); usb_write(0, USB_OUT_INT_EN); usb_write(0, USB_INT_EN); /* Enable interrupts */ usb_set(USB_IN_INT_EP0, USB_IN_INT_EN); usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN); /* Dont enable rest of the interrupts */ /* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN); usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ /* Enable SUSPEND */ usb_set(PM_ENABLE_SUSPEND, USB_PM); /* Enable the USB */ usb_set(PM_USB_ENABLE, USB_PM);#ifdef CONFIG_ARCH_LH7A404 /* NOTE: DOES NOT WORK! */ /* Let host detect UDC: * Software must write a 0 to the PMR:DCP_CTRL bit to turn this * transistor on and pull the USBDP pin HIGH. */ /* usb_clear(PM_USB_DCP, USB_PM); usb_set(PM_USB_DCP, USB_PM); */#endif}/* Register entry point for the peripheral controller driver.*/int usb_gadget_register_driver(struct usb_gadget_driver *driver){ struct lh7a40x_udc *dev = the_controller; int retval; DEBUG("%s: %s\n", __FUNCTION__, driver->driver.name); if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind || !driver->unbind || !driver->disconnect || !driver->setup) return -EINVAL; if (!dev) return -ENODEV; if (dev->driver) return -EBUSY; /* first hook up the driver ... */ dev->driver = driver; dev->gadget.dev.driver = &driver->driver; device_add(&dev->gadget.dev); retval = driver->bind(&dev->gadget); if (retval) { printk("%s: bind to driver %s --> error %d\n", dev->gadget.name, driver->driver.name, retval); device_del(&dev->gadget.dev); dev->driver = 0; dev->gadget.dev.driver = 0; return retval; } /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. * NOTE: this shouldn't power up until later. */ printk("%s: registered gadget driver '%s'\n", dev->gadget.name, driver->driver.name); udc_enable(dev); return 0;}EXPORT_SYMBOL(usb_gadget_register_driver);/* Unregister entry point for the peripheral controller driver.*/int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){ struct lh7a40x_udc *dev = the_controller; unsigned long flags; if (!dev) return -ENODEV; if (!driver || driver != dev->driver) return -EINVAL; spin_lock_irqsave(&dev->lock, flags); dev->driver = 0; stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); driver->unbind(&dev->gadget); device_del(&dev->gadget.dev); udc_disable(dev); DEBUG("unregistered gadget driver '%s'\n", driver->driver.name); return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*//** Write request to FIFO (max write == maxp size) * Return: 0 = still running, 1 = completed, negative = errno * NOTE: INDEX register must be set for EP */static int write_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req){ u32 max; u32 csr; max = le16_to_cpu(ep->desc->wMaxPacketSize); csr = usb_read(ep->csr1); DEBUG("CSR: %x %d\n", csr, csr & USB_IN_CSR1_FIFO_NOT_EMPTY); if (!(csr & USB_IN_CSR1_FIFO_NOT_EMPTY)) { unsigned count; int is_last, is_short; count = write_packet(ep, req, max); usb_set(USB_IN_CSR1_IN_PKT_RDY, ep->csr1); /* last packet is usually short (or a zlp) */ if (unlikely(count != max)) is_last = is_short = 1; else { if (likely(req->req.length != req->req.actual) || req->req.zero) is_last = 0; else is_last = 1; /* interrupt/iso maxpacket may not fill the fifo */ is_short = unlikely(max < ep_maxpacket(ep)); } DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, ep->ep.name, count, is_last ? "/L" : "", is_short ? "/S" : "", req->req.length - req->req.actual, req); /* requests complete when all IN data is in the FIFO */ if (is_last) { done(ep, req, 0); if (list_empty(&ep->queue)) { pio_irq_disable(ep_index(ep)); } return 1; } } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -