*
*/
-// #define VERBOSE DBG_VERBOSE
+/* #define VERBOSE_DEBUG */
#include <linux/device.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
#include <asm/byteorder.h>
#include <asm/dma.h>
#include <asm/hardware.h>
#include <linux/usb/ch9.h>
-#include <linux/usb_gadget.h>
+#include <linux/usb/gadget.h>
#include <asm/mach/udc_pxa2xx.h>
#error "Can't configure both IXP and PXA"
#endif
+/* IXP doesn't yet support <linux/clk.h> */
+#define clk_get(dev,name) NULL
+#define clk_enable(clk) do { } while (0)
+#define clk_disable(clk) do { } while (0)
+#define clk_put(clk) do { } while (0)
+
#endif
#include "pxa2xx_udc.h"
{
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
- if (mach->gpio_vbus)
- return gpio_get_value(mach->gpio_vbus);
+ if (mach->gpio_vbus) {
+ int value = gpio_get_value(mach->gpio_vbus);
+ return mach->gpio_vbus_inverted ? !value : value;
+ }
if (mach->udc_is_connected)
return mach->udc_is_connected();
return 1;
/* kickstart this i/o queue? */
if (list_empty(&ep->queue) && !ep->stopped) {
- if (ep->desc == 0 /* ep0 */) {
+ if (ep->desc == NULL/* ep0 */) {
unsigned length = _req->length;
switch (dev->ep0state) {
}
/* pio or dma irq handler advances the queue. */
- if (likely (req != 0))
+ if (likely(req != NULL))
list_add_tail(&req->queue, &ep->queue);
local_irq_restore(flags);
/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
* in active use.
*/
-static int pullup(struct pxa2xx_udc *udc, int is_active)
+static int pullup(struct pxa2xx_udc *udc)
{
- is_active = is_active && udc->vbus && udc->pullup;
+ int is_active = udc->vbus && udc->pullup && !udc->suspended;
DMSG("%s\n", is_active ? "active" : "inactive");
- if (is_active)
- udc_enable(udc);
- else {
- if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
- DMSG("disconnect %s\n", udc->driver
- ? udc->driver->driver.name
- : "(no driver)");
- stop_activity(udc, udc->driver);
+ if (is_active) {
+ if (!udc->active) {
+ udc->active = 1;
+ /* Enable clock for USB device */
+ clk_enable(udc->clk);
+ udc_enable(udc);
+ }
+ } else {
+ if (udc->active) {
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+ DMSG("disconnect %s\n", udc->driver
+ ? udc->driver->driver.name
+ : "(no driver)");
+ stop_activity(udc, udc->driver);
+ }
+ udc_disable(udc);
+ /* Disable clock for USB device */
+ clk_disable(udc->clk);
+ udc->active = 0;
}
- udc_disable(udc);
+
}
return 0;
}
struct pxa2xx_udc *udc;
udc = container_of(_gadget, struct pxa2xx_udc, gadget);
- udc->vbus = is_active = (is_active != 0);
+ udc->vbus = (is_active != 0);
DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
- pullup(udc, is_active);
+ pullup(udc);
return 0;
}
if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
return -EOPNOTSUPP;
- is_active = (is_active != 0);
- udc->pullup = is_active;
- pullup(udc, is_active);
+ udc->pullup = (is_active != 0);
+ pullup(udc);
return 0;
}
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-static const char proc_node_name [] = "driver/udc";
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
static int
-udc_proc_read(char *page, char **start, off_t off, int count,
- int *eof, void *_dev)
+udc_seq_show(struct seq_file *m, void *_d)
{
- char *buf = page;
- struct pxa2xx_udc *dev = _dev;
- char *next = buf;
- unsigned size = count;
+ struct pxa2xx_udc *dev = m->private;
unsigned long flags;
- int i, t;
+ int i;
u32 tmp;
- if (off != 0)
- return 0;
-
local_irq_save(flags);
/* basic device status */
- t = scnprintf(next, size, DRIVER_DESC "\n"
+ seq_printf(m, DRIVER_DESC "\n"
"%s version: %s\nGadget driver: %s\nHost %s\n\n",
driver_name, DRIVER_VERSION SIZE_STR "(pio)",
dev->driver ? dev->driver->driver.name : "(none)",
is_vbus_present() ? "full speed" : "disconnected");
- size -= t;
- next += t;
/* registers for device and ep0 */
- t = scnprintf(next, size,
+ seq_printf(m,
"uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
- size -= t;
- next += t;
tmp = UDCCR;
- t = scnprintf(next, size,
+ seq_printf(m,
"udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
(tmp & UDCCR_REM) ? " rem" : "",
(tmp & UDCCR_RSTIR) ? " rstir" : "",
(tmp & UDCCR_RSM) ? " rsm" : "",
(tmp & UDCCR_UDA) ? " uda" : "",
(tmp & UDCCR_UDE) ? " ude" : "");
- size -= t;
- next += t;
tmp = UDCCS0;
- t = scnprintf(next, size,
+ seq_printf(m,
"udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
(tmp & UDCCS0_SA) ? " sa" : "",
(tmp & UDCCS0_RNE) ? " rne" : "",
(tmp & UDCCS0_FTF) ? " ftf" : "",
(tmp & UDCCS0_IPR) ? " ipr" : "",
(tmp & UDCCS0_OPR) ? " opr" : "");
- size -= t;
- next += t;
if (dev->has_cfr) {
tmp = UDCCFR;
- t = scnprintf(next, size,
+ seq_printf(m,
"udccfr %02X =%s%s\n", tmp,
(tmp & UDCCFR_AREN) ? " aren" : "",
(tmp & UDCCFR_ACM) ? " acm" : "");
- size -= t;
- next += t;
}
if (!is_vbus_present() || !dev->driver)
goto done;
- t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+ seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
dev->stats.write.bytes, dev->stats.write.ops,
dev->stats.read.bytes, dev->stats.read.ops,
dev->stats.irqs);
- size -= t;
- next += t;
/* dump endpoint queues */
for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
struct pxa2xx_request *req;
if (i != 0) {
- const struct usb_endpoint_descriptor *d;
+ const struct usb_endpoint_descriptor *desc;
- d = ep->desc;
- if (!d)
+ desc = ep->desc;
+ if (!desc)
continue;
tmp = *dev->ep [i].reg_udccs;
- t = scnprintf(next, size,
+ seq_printf(m,
"%s max %d %s udccs %02x irqs %lu\n",
- ep->ep.name, le16_to_cpu (d->wMaxPacketSize),
+ ep->ep.name, le16_to_cpu(desc->wMaxPacketSize),
"pio", tmp, ep->pio_irqs);
/* TODO translate all five groups of udccs bits! */
} else /* ep0 should only have one transfer queued */
- t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
+ seq_printf(m, "ep0 max 16 pio irqs %lu\n",
ep->pio_irqs);
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
if (list_empty(&ep->queue)) {
- t = scnprintf(next, size, "\t(nothing queued)\n");
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
+ seq_printf(m, "\t(nothing queued)\n");
continue;
}
list_for_each_entry(req, &ep->queue, queue) {
- t = scnprintf(next, size,
+ seq_printf(m,
"\treq %p len %d/%d buf %p\n",
&req->req, req->req.actual,
req->req.length, req->req.buf);
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
}
}
done:
local_irq_restore(flags);
- *eof = 1;
- return count - size;
+ return 0;
+}
+
+static int
+udc_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, udc_seq_show, inode->i_private);
}
-#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)
+static const struct file_operations debug_fops = {
+ .open = udc_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+#define create_debug_files(dev) \
+ do { \
+ dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
+ S_IRUGO, NULL, dev, &debug_fops); \
+ } while (0)
+#define remove_debug_files(dev) \
+ do { \
+ if (dev->debugfs_udc) \
+ debugfs_remove(dev->debugfs_udc); \
+ } while (0)
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
-#define create_proc_files() do {} while (0)
-#define remove_proc_files() do {} while (0)
+#define create_debug_files(dev) do {} while (0)
+#define remove_debug_files(dev) do {} while (0)
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
udc_clear_mask_UDCCR(UDCCR_UDE);
-#ifdef CONFIG_ARCH_PXA
- /* Disable clock for USB device */
- pxa_set_cken(CKEN_USB, 0);
-#endif
-
ep0_idle (dev);
dev->gadget.speed = USB_SPEED_UNKNOWN;
}
{
udc_clear_mask_UDCCR(UDCCR_UDE);
-#ifdef CONFIG_ARCH_PXA
- /* Enable clock for USB device */
- pxa_set_cken(CKEN_USB, 1);
- udelay(5);
-#endif
-
/* try to clear these bits before we enable the udc */
udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR);
* for set_configuration as well as eventual disconnect.
*/
DMSG("registered gadget driver '%s'\n", driver->driver.name);
- pullup(dev, 1);
+ pullup(dev);
dump_state(dev);
return 0;
}
return -EINVAL;
local_irq_disable();
- pullup(dev, 0);
+ dev->pullup = 0;
+ pullup(dev);
stop_activity(dev, driver);
local_irq_enable();
driver->unbind(&dev->gadget);
+ dev->gadget.dev.driver = NULL;
dev->driver = NULL;
device_del (&dev->gadget.dev);
struct pxa2xx_udc *dev = _dev;
int vbus = gpio_get_value(dev->mach->gpio_vbus);
+ if (dev->mach->gpio_vbus_inverted)
+ vbus = !vbus;
+
pxa2xx_udc_vbus_session(&dev->gadget, vbus);
return IRQ_HANDLED;
}
/* insist on Intel/ARM/XScale */
asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev));
if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
- printk(KERN_ERR "%s: not XScale!\n", driver_name);
+ pr_err("%s: not XScale!\n", driver_name);
return -ENODEV;
}
break;
#endif
default:
- printk(KERN_ERR "%s: unrecognized processor: %08x\n",
+ pr_err("%s: unrecognized processor: %08x\n",
driver_name, chiprev);
/* iop3xx, ixp4xx, ... */
return -ENODEV;
if (irq < 0)
return -ENODEV;
+ dev->clk = clk_get(&pdev->dev, "UDCCLK");
+ if (IS_ERR(dev->clk)) {
+ retval = PTR_ERR(dev->clk);
+ goto err_clk;
+ }
+
pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
dev->has_cfr ? "" : " (!cfr)",
SIZE_STR "(pio)"
dev_dbg(&pdev->dev,
"can't get vbus gpio %d, err: %d\n",
dev->mach->gpio_vbus, retval);
- return -EBUSY;
+ goto err_gpio_vbus;
}
gpio_direction_input(dev->mach->gpio_vbus);
vbus_irq = gpio_to_irq(dev->mach->gpio_vbus);
- set_irq_type(vbus_irq, IRQT_BOTHEDGE);
} else
vbus_irq = 0;
dev_dbg(&pdev->dev,
"can't get pullup gpio %d, err: %d\n",
dev->mach->gpio_pullup, retval);
- if (dev->mach->gpio_vbus)
- gpio_free(dev->mach->gpio_vbus);
- return -EBUSY;
+ goto err_gpio_pullup;
}
gpio_direction_output(dev->mach->gpio_pullup, 0);
}
retval = request_irq(irq, pxa2xx_udc_irq,
IRQF_DISABLED, driver_name, dev);
if (retval != 0) {
- printk(KERN_ERR "%s: can't get irq %d, err %d\n",
+ pr_err("%s: can't get irq %d, err %d\n",
driver_name, irq, retval);
- if (dev->mach->gpio_pullup)
- gpio_free(dev->mach->gpio_pullup);
- if (dev->mach->gpio_vbus)
- gpio_free(dev->mach->gpio_vbus);
- return -EBUSY;
+ goto err_irq1;
}
dev->got_irq = 1;
IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
driver_name, dev);
if (retval != 0) {
- printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
lubbock_fail0:
- free_irq(irq, dev);
- if (dev->mach->gpio_pullup)
- gpio_free(dev->mach->gpio_pullup);
- if (dev->mach->gpio_vbus)
- gpio_free(dev->mach->gpio_vbus);
- return -EBUSY;
+ goto err_irq_lub;
}
retval = request_irq(LUBBOCK_USB_IRQ,
lubbock_vbus_irq,
IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
driver_name, dev);
if (retval != 0) {
- printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_IRQ, retval);
free_irq(LUBBOCK_USB_DISC_IRQ, dev);
goto lubbock_fail0;
#endif
if (vbus_irq) {
retval = request_irq(vbus_irq, udc_vbus_irq,
- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+ IRQF_DISABLED | IRQF_SAMPLE_RANDOM |
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
driver_name, dev);
if (retval != 0) {
- printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ pr_err("%s: can't get irq %i, err %d\n",
driver_name, vbus_irq, retval);
- free_irq(irq, dev);
- if (dev->mach->gpio_pullup)
- gpio_free(dev->mach->gpio_pullup);
- if (dev->mach->gpio_vbus)
- gpio_free(dev->mach->gpio_vbus);
- return -EBUSY;
+ goto err_vbus_irq;
}
}
- create_proc_files();
+ create_debug_files(dev);
return 0;
+
+ err_vbus_irq:
+#ifdef CONFIG_ARCH_LUBBOCK
+ free_irq(LUBBOCK_USB_DISC_IRQ, dev);
+ err_irq_lub:
+#endif
+ free_irq(irq, dev);
+ err_irq1:
+ if (dev->mach->gpio_pullup)
+ gpio_free(dev->mach->gpio_pullup);
+ err_gpio_pullup:
+ if (dev->mach->gpio_vbus)
+ gpio_free(dev->mach->gpio_vbus);
+ err_gpio_vbus:
+ clk_put(dev->clk);
+ err_clk:
+ return retval;
}
static void pxa2xx_udc_shutdown(struct platform_device *_dev)
if (dev->driver)
return -EBUSY;
- udc_disable(dev);
- remove_proc_files();
+ dev->pullup = 0;
+ pullup(dev);
+
+ remove_debug_files(dev);
if (dev->got_irq) {
free_irq(platform_get_irq(pdev, 0), dev);
if (dev->mach->gpio_pullup)
gpio_free(dev->mach->gpio_pullup);
+ clk_put(dev->clk);
+
platform_set_drvdata(pdev, NULL);
the_controller = NULL;
return 0;
static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state)
{
struct pxa2xx_udc *udc = platform_get_drvdata(dev);
+ unsigned long flags;
if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
WARN("USB host won't detect disconnect!\n");
- pullup(udc, 0);
+ udc->suspended = 1;
+
+ local_irq_save(flags);
+ pullup(udc);
+ local_irq_restore(flags);
return 0;
}
static int pxa2xx_udc_resume(struct platform_device *dev)
{
struct pxa2xx_udc *udc = platform_get_drvdata(dev);
+ unsigned long flags;
- pullup(udc, 1);
+ udc->suspended = 0;
+ local_irq_save(flags);
+ pullup(udc);
+ local_irq_restore(flags);
return 0;
}
static int __init udc_init(void)
{
- printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
+ pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
return platform_driver_probe(&udc_driver, pxa2xx_udc_probe);
}
module_init(udc_init);