📄 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 "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))
#endif
struct 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_STATES
static 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, int);
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 *,
int);
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 *, int);
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_FILES
static const char proc_node_name[] = "driver/udc";
static int
udc_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 {
DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -