* Boston, MA 02111-1307, USA.
*/
-#undef DEBUG
-#undef VERBOSE
+#undef VERBOSE_DEBUG
#undef PACKET_TRACE
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/usb/ch9.h>
-#include <linux/usb_gadget.h>
+#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
-#include <asm/hardware.h>
+#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
-#include <asm/mach-types.h>
+#include <asm/gpio.h>
-#include <asm/arch/gpio.h>
-#include <asm/arch/board.h>
-#include <asm/arch/cpu.h>
-#include <asm/arch/at91sam9261_matrix.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
+#include <mach/at91sam9261_matrix.h>
#include "at91_udc.h"
}
static const struct file_operations proc_ops = {
+ .owner = THIS_MODULE,
.open = proc_udc_open,
.read = seq_read,
.llseek = seq_lseek,
static void create_debug_file(struct at91_udc *udc)
{
- struct proc_dir_entry *pde;
-
- pde = create_proc_entry (debug_filename, 0, NULL);
- udc->pde = pde;
- if (pde == NULL)
- return;
-
- pde->proc_fops = &proc_ops;
- pde->data = udc;
+ udc->pde = proc_create_data(debug_filename, 0, NULL, &proc_ops, udc);
}
static void remove_debug_file(struct at91_udc *udc)
u32 csr = __raw_readl(creg);
u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
unsigned total, count, is_last;
+ u8 *buf;
/*
* TODO: allow for writing two packets to the fifo ... that'll
return 0;
}
+ buf = req->req.buf + req->req.actual;
+ prefetch(buf);
total = req->req.length - req->req.actual;
if (ep->ep.maxpacket < total) {
count = ep->ep.maxpacket;
* recover when the actual bytecount matters (e.g. for USB Test
* and Measurement Class devices).
*/
- __raw_writesb(dreg, req->req.buf + req->req.actual, count);
+ __raw_writesb(dreg, buf, count);
csr &= ~SET_FX;
csr |= CLR_FX | AT91_UDP_TXPKTRDY;
__raw_writel(csr, creg);
if (list_empty(&ep->queue))
return;
- VDBG("%s %s\n", __FUNCTION__, ep->ep.name);
+ VDBG("%s %s\n", __func__, ep->ep.name);
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct at91_request, queue);
done(ep, req, status);
return -ESHUTDOWN;
}
- tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ tmp = usb_endpoint_type(desc);
switch (tmp) {
case USB_ENDPOINT_XFER_CONTROL:
DBG("only one control endpoint\n");
local_irq_save(flags);
/* initialize endpoint to match this descriptor */
- ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+ ep->is_in = usb_endpoint_dir_in(desc);
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
ep->stopped = 0;
if (ep->is_in)
*/
static struct usb_request *
-at91_ep_alloc_request(struct usb_ep *_ep, unsigned int gfp_flags)
+at91_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct at91_request *req;
kfree(req);
}
-static void *at91_ep_alloc_buffer(
- struct usb_ep *_ep,
- unsigned bytes,
- dma_addr_t *dma,
- gfp_t gfp_flags)
-{
- *dma = ~0;
- return kmalloc(bytes, gfp_flags);
-}
-
-static void at91_ep_free_buffer(
- struct usb_ep *ep,
- void *buf,
- dma_addr_t dma,
- unsigned bytes)
-{
- kfree(buf);
-}
-
static int at91_ep_queue(struct usb_ep *_ep,
struct usb_request *_req, gfp_t gfp_flags)
{
.disable = at91_ep_disable,
.alloc_request = at91_ep_alloc_request,
.free_request = at91_ep_free_request,
- .alloc_buffer = at91_ep_alloc_buffer,
- .free_buffer = at91_ep_free_buffer,
.queue = at91_ep_queue,
.dequeue = at91_ep_dequeue,
.set_halt = at91_ep_set_halt,
int status = -EINVAL;
unsigned long flags;
- DBG("%s\n", __FUNCTION__ );
+ DBG("%s\n", __func__ );
local_irq_save(flags);
if (!udc->clocked || !udc->suspended)
*/
static void pullup(struct at91_udc *udc, int is_on)
{
+ int active = !udc->board.pullup_active_low;
+
if (!udc->enabled || !udc->vbus)
is_on = 0;
DBG("%sactive\n", is_on ? "" : "in");
if (is_on) {
clk_on(udc);
+ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
at91_udp_write(udc, AT91_UDP_TXVC, 0);
if (cpu_is_at91rm9200())
- at91_set_gpio_value(udc->board.pullup_pin, 1);
- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
+ gpio_set_value(udc->board.pullup_pin, active);
+ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
txvc |= AT91_UDP_TXVC_PUON;
at91_udp_write(udc, AT91_UDP_TXVC, txvc);
- } else if (cpu_is_at91sam9261()) {
+ } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
u32 usbpucr;
usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR);
}
} else {
stop_activity(udc);
+ at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
if (cpu_is_at91rm9200())
- at91_set_gpio_value(udc->board.pullup_pin, 0);
- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
+ gpio_set_value(udc->board.pullup_pin, !active);
+ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
txvc &= ~AT91_UDP_TXVC_PUON;
at91_udp_write(udc, AT91_UDP_TXVC, txvc);
- } else if (cpu_is_at91sam9261()) {
+ } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
u32 usbpucr;
usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR);
| USB_REQ_GET_STATUS:
tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[tmp];
- if (tmp > NUM_ENDPOINTS || (tmp && !ep->desc))
+ if (tmp >= NUM_ENDPOINTS || (tmp && !ep->desc))
goto stall;
if (tmp) {
| USB_REQ_SET_FEATURE:
tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[tmp];
- if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)
+ if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
goto stall;
if (!ep->desc || ep->is_iso)
goto stall;
| USB_REQ_CLEAR_FEATURE:
tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[tmp];
- if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)
+ if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
goto stall;
if (tmp == 0)
goto succeed;
{
struct at91_udc *udc = _udc;
u32 rescans = 5;
+ int disable_clock = 0;
+
+ if (!udc->clocked) {
+ clk_on(udc);
+ disable_clock = 1;
+ }
while (rescans--) {
u32 status;
}
}
+ if (disable_clock)
+ clk_off(udc);
+
return IRQ_HANDLED;
}
.ep0 = &controller.ep[0].ep,
.name = driver_name,
.dev = {
- .bus_id = "gadget",
+ .init_name = "gadget",
.release = nop_release,
}
},
/* vbus needs at least brief debouncing */
udelay(10);
- value = at91_get_gpio_value(udc->board.vbus_pin);
+ value = gpio_get_value(udc->board.vbus_pin);
if (value != udc->vbus)
at91_vbus_session(&udc->gadget, value);
udc->driver = driver;
udc->gadget.dev.driver = &driver->driver;
- udc->gadget.dev.driver_data = &driver->driver;
+ dev_set_drvdata(&udc->gadget.dev, &driver->driver);
udc->enabled = 1;
udc->selfpowered = 1;
DBG("driver->bind() returned %d\n", retval);
udc->driver = NULL;
udc->gadget.dev.driver = NULL;
- udc->gadget.dev.driver_data = NULL;
+ dev_set_drvdata(&udc->gadget.dev, NULL);
udc->enabled = 0;
udc->selfpowered = 0;
return retval;
local_irq_enable();
driver->unbind(&udc->gadget);
+ udc->gadget.dev.driver = NULL;
+ dev_set_drvdata(&udc->gadget.dev, NULL);
udc->driver = NULL;
DBG("unbound from %s\n", driver->driver.name);
}
if (pdev->num_resources != 2) {
- DBG("invalid num_resources");
+ DBG("invalid num_resources\n");
return -ENODEV;
}
if ((pdev->resource[0].flags != IORESOURCE_MEM)
|| (pdev->resource[1].flags != IORESOURCE_IRQ)) {
- DBG("invalid resource type");
+ DBG("invalid resource type\n");
return -ENODEV;
}
if (!res)
return -ENXIO;
- if (!request_mem_region(res->start,
- res->end - res->start + 1,
- driver_name)) {
+ if (!request_mem_region(res->start, resource_size(res), driver_name)) {
DBG("someone's using UDC memory\n");
return -EBUSY;
}
udc->pdev = pdev;
udc->enabled = 0;
- udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1);
+ /* rm9200 needs manual D+ pullup; off by default */
+ if (cpu_is_at91rm9200()) {
+ if (udc->board.pullup_pin <= 0) {
+ DBG("no D+ pullup?\n");
+ retval = -ENODEV;
+ goto fail0;
+ }
+ retval = gpio_request(udc->board.pullup_pin, "udc_pullup");
+ if (retval) {
+ DBG("D+ pullup is busy\n");
+ goto fail0;
+ }
+ gpio_direction_output(udc->board.pullup_pin,
+ udc->board.pullup_active_low);
+ }
+
+ /* newer chips have more FIFO memory than rm9200 */
+ if (cpu_is_at91sam9260()) {
+ udc->ep[0].maxpacket = 64;
+ udc->ep[3].maxpacket = 64;
+ udc->ep[4].maxpacket = 512;
+ udc->ep[5].maxpacket = 512;
+ } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
+ udc->ep[3].maxpacket = 64;
+ } else if (cpu_is_at91sam9263()) {
+ udc->ep[0].maxpacket = 64;
+ udc->ep[3].maxpacket = 64;
+ }
+
+ udc->udp_baseaddr = ioremap(res->start, resource_size(res));
if (!udc->udp_baseaddr) {
- release_mem_region(res->start, res->end - res->start + 1);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto fail0a;
}
udc_reinit(udc);
if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) {
DBG("clocks missing\n");
retval = -ENODEV;
- goto fail0;
+ /* NOTE: we "know" here that refcounts on these are NOPs */
+ goto fail0b;
}
retval = device_register(&udc->gadget.dev);
if (retval < 0)
- goto fail0;
+ goto fail0b;
/* don't do anything until we have both gadget driver and VBUS */
clk_enable(udc->iclk);
/* request UDC and maybe VBUS irqs */
udc->udp_irq = platform_get_irq(pdev, 0);
- if (request_irq(udc->udp_irq, at91_udc_irq,
- IRQF_DISABLED, driver_name, udc)) {
+ retval = request_irq(udc->udp_irq, at91_udc_irq,
+ IRQF_DISABLED, driver_name, udc);
+ if (retval < 0) {
DBG("request irq %d failed\n", udc->udp_irq);
- retval = -EBUSY;
goto fail1;
}
if (udc->board.vbus_pin > 0) {
+ retval = gpio_request(udc->board.vbus_pin, "udc_vbus");
+ if (retval < 0) {
+ DBG("request vbus pin failed\n");
+ goto fail2;
+ }
+ gpio_direction_input(udc->board.vbus_pin);
+
/*
* Get the initial state of VBUS - we cannot expect
* a pending interrupt.
*/
- udc->vbus = at91_get_gpio_value(udc->board.vbus_pin);
+ udc->vbus = gpio_get_value(udc->board.vbus_pin);
if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
IRQF_DISABLED, driver_name, udc)) {
DBG("request vbus irq %d failed\n",
udc->board.vbus_pin);
- free_irq(udc->udp_irq, udc);
retval = -EBUSY;
- goto fail1;
+ goto fail3;
}
} else {
DBG("no VBUS detection, assuming always-on\n");
INFO("%s version %s\n", driver_name, DRIVER_VERSION);
return 0;
+fail3:
+ if (udc->board.vbus_pin > 0)
+ gpio_free(udc->board.vbus_pin);
+fail2:
+ free_irq(udc->udp_irq, udc);
fail1:
device_unregister(&udc->gadget.dev);
+fail0b:
+ iounmap(udc->udp_baseaddr);
+fail0a:
+ if (cpu_is_at91rm9200())
+ gpio_free(udc->board.pullup_pin);
fail0:
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
DBG("%s probe failed, %d\n", driver_name, retval);
return retval;
}
device_init_wakeup(&pdev->dev, 0);
remove_debug_file(udc);
- if (udc->board.vbus_pin > 0)
+ if (udc->board.vbus_pin > 0) {
free_irq(udc->board.vbus_pin, udc);
+ gpio_free(udc->board.vbus_pin);
+ }
free_irq(udc->udp_irq, udc);
device_unregister(&udc->gadget.dev);
iounmap(udc->udp_baseaddr);
+
+ if (cpu_is_at91rm9200())
+ gpio_free(udc->board.pullup_pin);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
clk_put(udc->iclk);
clk_put(udc->fclk);
MODULE_DESCRIPTION("AT91 udc driver");
MODULE_AUTHOR("Thomas Rathbone, David Brownell");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_udc");