diff options
author | Tom Rini <trini@ti.com> | 2015-01-20 10:20:13 -0500 |
---|---|---|
committer | Tom Rini <trini@ti.com> | 2015-01-20 10:20:13 -0500 |
commit | b44a414959e3a32576a243aaa4966c1a9565ccfa (patch) | |
tree | 96308baa495f95e394013cba467055a83b4b0d8d /drivers/usb | |
parent | b9315890f62a05af74a78daf98b55f0268297cbf (diff) | |
parent | 119339759266b81af1aa8883702b6b651d7752dd (diff) |
Merge branch 'master' of git://git.denx.de/u-boot-usb
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/eth/asix88179.c | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/composite.c | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/f_dfu.c | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa25x_udc.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 4 | ||||
-rw-r--r-- | drivers/usb/musb-new/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_host.c | 12 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_host.h | 1 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_regs.h | 92 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_uboot.c | 178 | ||||
-rw-r--r-- | drivers/usb/musb-new/sunxi.c | 279 | ||||
-rw-r--r-- | drivers/usb/musb-new/usb-compat.h | 1 |
12 files changed, 544 insertions, 58 deletions
diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c index b8ca720e25..0ef85db7b5 100644 --- a/drivers/usb/eth/asix88179.c +++ b/drivers/usb/eth/asix88179.c @@ -271,6 +271,19 @@ static int asix_read_mac(struct eth_device *eth) return 0; } +static int asix_write_mac(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int ret; + + ret = asix_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, eth->enetaddr); + if (ret < 0) + debug("Failed to set MAC address: %02x\n", ret); + + return ret; +} + static int asix_basic_reset(struct ueth_data *dev) { struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; @@ -686,6 +699,7 @@ int ax88179_eth_get_info(struct usb_device *dev, struct ueth_data *ss, eth->send = asix_send; eth->recv = asix_recv; eth->halt = asix_halt; + eth->write_hwaddr = asix_write_mac; eth->priv = ss; if (asix_basic_reset(ss)) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a4c5606527..98c2da6f14 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -761,6 +761,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value >= 0) value = min(w_length, (u16) value); break; + case USB_DT_BOS: + /* + * The USB compliance test (USB 2.0 Command Verifier) + * issues this request. We should not run into the + * default path here. But return for now until + * the superspeed support is added. + */ + break; default: goto unknown; } diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index ead71eba6b..77a1567a94 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -780,6 +780,13 @@ static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return 0; } +static int __dfu_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + return f_dfu->altsetting; +} + /* TODO: is this really what we need here? */ static void dfu_disable(struct usb_function *f) { @@ -806,6 +813,7 @@ static int dfu_bind_config(struct usb_configuration *c) f_dfu->usb_function.bind = dfu_bind; f_dfu->usb_function.unbind = dfu_unbind; f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.get_alt = __dfu_get_alt; f_dfu->usb_function.disable = dfu_disable; f_dfu->usb_function.strings = dfu_generic_strings; f_dfu->usb_function.setup = dfu_handle; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 8945c5b665..d4460b2dc7 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1950,11 +1950,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev->watchdog.period = 5000 * CONFIG_SYS_HZ / 1000000; /* 5 ms */ dev->watchdog.function = udc_watchdog; + dev->mach = &mach_info; + udc_disable(dev); udc_reinit(dev); - dev->mach = &mach_info; - dev->gadget.name = "pxa2xx_udc"; retval = driver->bind(&dev->gadget); if (retval) { diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bc7606646b..f1fb190132 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1148,7 +1148,7 @@ disable_periodic(struct ehci_ctrl *ctrl) struct int_queue * create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, - int elementsize, void *buffer) + int elementsize, void *buffer, int interval) { struct ehci_ctrl *ctrl = dev->controller; struct int_queue *result = NULL; @@ -1398,7 +1398,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - queue = create_int_queue(dev, pipe, 1, length, buffer); + queue = create_int_queue(dev, pipe, 1, length, buffer, interval); if (!queue) return -1; diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 3facf0fc10..9edeece381 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o +obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o ccflags-y := $(call cc-option,-Wno-unused-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \ diff --git a/drivers/usb/musb-new/musb_host.c b/drivers/usb/musb-new/musb_host.c index bbcee88241..437309ceb4 100644 --- a/drivers/usb/musb-new/musb_host.c +++ b/drivers/usb/musb-new/musb_host.c @@ -2130,8 +2130,6 @@ done: return ret; } - -#ifndef __UBOOT__ /* * abort a transfer that's at the head of a hardware queue. * called with controller locked, irqs blocked @@ -2195,7 +2193,14 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) return status; } -static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +#ifndef __UBOOT__ +static int musb_urb_dequeue( +#else +int musb_urb_dequeue( +#endif + struct usb_hcd *hcd, + struct urb *urb, + int status) { struct musb *musb = hcd_to_musb(hcd); struct musb_qh *qh; @@ -2253,6 +2258,7 @@ done: return ret; } +#ifndef __UBOOT__ /* disable an endpoint */ static void musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) diff --git a/drivers/usb/musb-new/musb_host.h b/drivers/usb/musb-new/musb_host.h index ebebe0c02a..546b4a2715 100644 --- a/drivers/usb/musb-new/musb_host.h +++ b/drivers/usb/musb-new/musb_host.h @@ -110,5 +110,6 @@ static inline struct urb *next_urb(struct musb_qh *qh) #ifdef __UBOOT__ int musb_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); +int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); #endif #endif /* _MUSB_HOST_H */ diff --git a/drivers/usb/musb-new/musb_regs.h b/drivers/usb/musb-new/musb_regs.h index 03f2655af2..27e4ed4ec6 100644 --- a/drivers/usb/musb-new/musb_regs.h +++ b/drivers/usb/musb-new/musb_regs.h @@ -216,6 +216,9 @@ #ifndef CONFIG_BLACKFIN +/* SUNXI has different reg addresses, but identical r/w functions */ +#ifndef CONFIG_ARCH_SUNXI + /* * Common USB registers */ @@ -318,6 +321,85 @@ #define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ (0x80 + (8*(_epnum)) + (_offset)) +#else /* CONFIG_ARCH_SUNXI */ + +/* + * Common USB registers + */ + +#define MUSB_FADDR 0x0098 +#define MUSB_POWER 0x0040 + +#define MUSB_INTRTX 0x0044 +#define MUSB_INTRRX 0x0046 +#define MUSB_INTRTXE 0x0048 +#define MUSB_INTRRXE 0x004A +#define MUSB_INTRUSB 0x004C +#define MUSB_INTRUSBE 0x0050 +#define MUSB_FRAME 0x0054 +#define MUSB_INDEX 0x0042 +#define MUSB_TESTMODE 0x007C + +/* Get offset for a given FIFO from musb->mregs */ +#define MUSB_FIFO_OFFSET(epnum) (0x00 + ((epnum) * 4)) + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL 0x0041 + +/* These are always controlled through the INDEX register */ +#define MUSB_TXFIFOSZ 0x0090 +#define MUSB_RXFIFOSZ 0x0094 +#define MUSB_TXFIFOADD 0x0092 +#define MUSB_RXFIFOADD 0x0096 + +#define MUSB_EPINFO 0x0078 +#define MUSB_RAMINFO 0x0079 +#define MUSB_LINKINFO 0x007A +#define MUSB_VPLEN 0x007B +#define MUSB_HS_EOF1 0x007C +#define MUSB_FS_EOF1 0x007D +#define MUSB_LS_EOF1 0x007E + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x0080 +#define MUSB_TXCSR 0x0082 +#define MUSB_CSR0 0x0082 +#define MUSB_RXMAXP 0x0084 +#define MUSB_RXCSR 0x0086 +#define MUSB_RXCOUNT 0x0088 +#define MUSB_COUNT0 0x0088 +#define MUSB_TXTYPE 0x008C +#define MUSB_TYPE0 0x008C +#define MUSB_TXINTERVAL 0x008D +#define MUSB_NAKLIMIT0 0x008D +#define MUSB_RXTYPE 0x008E +#define MUSB_RXINTERVAL 0x008F + +#define MUSB_CONFIGDATA 0x00b0 /* musb_read_configdata adds 0x10 ! */ +#define MUSB_FIFOSIZE 0x0090 + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) (_offset) + +#define MUSB_TXCSR_MODE 0x2000 + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MUSB_TXFUNCADDR 0x0098 +#define MUSB_TXHUBADDR 0x009A +#define MUSB_TXHUBPORT 0x009B + +#define MUSB_RXFUNCADDR 0x009C +#define MUSB_RXHUBADDR 0x009E +#define MUSB_RXHUBPORT 0x009F + +/* Endpoint is selected with MUSB_INDEX. */ +#define MUSB_BUSCTL_OFFSET(_epnum, _offset) (_offset) + +#endif /* CONFIG_ARCH_SUNXI */ + static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -340,7 +422,9 @@ static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val) { +#ifndef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */ musb_writeb(mbase, MUSB_ULPI_BUSCONTROL, val); +#endif } static inline u8 musb_read_txfifosz(void __iomem *mbase) @@ -365,7 +449,11 @@ static inline u16 musb_read_rxfifoadd(void __iomem *mbase) static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) { +#ifdef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */ + return 0; +#else return musb_readb(mbase, MUSB_ULPI_BUSCONTROL); +#endif } static inline u8 musb_read_configdata(void __iomem *mbase) @@ -376,7 +464,11 @@ static inline u8 musb_read_configdata(void __iomem *mbase) static inline u16 musb_read_hwvers(void __iomem *mbase) { +#ifdef CONFIG_ARCH_SUNXI + return 0; /* Unknown version */ +#else return musb_readw(mbase, MUSB_HWVERS); +#endif } static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 2676f09c38..6e58ddf02c 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -12,6 +12,11 @@ #include "musb_gadget.h" #ifdef CONFIG_MUSB_HOST +struct int_queue { + struct usb_host_endpoint hep; + struct urb urb; +}; + static struct musb *host; static struct usb_hcd hcd; static enum usb_device_speed host_speed; @@ -25,45 +30,42 @@ static void musb_host_complete_urb(struct urb *urb) static struct usb_host_endpoint hep; static struct urb urb; -static struct urb *construct_urb(struct usb_device *dev, int endpoint_type, - unsigned long pipe, void *buffer, int len, - struct devrequest *setup, int interval) +static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep, + struct usb_device *dev, int endpoint_type, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int interval) { int epnum = usb_pipeendpoint(pipe); int is_in = usb_pipein(pipe); - memset(&urb, 0, sizeof(struct urb)); - memset(&hep, 0, sizeof(struct usb_host_endpoint)); - INIT_LIST_HEAD(&hep.urb_list); - INIT_LIST_HEAD(&urb.urb_list); - urb.ep = &hep; - urb.complete = musb_host_complete_urb; - urb.status = -EINPROGRESS; - urb.dev = dev; - urb.pipe = pipe; - urb.transfer_buffer = buffer; - urb.transfer_dma = (unsigned long)buffer; - urb.transfer_buffer_length = len; - urb.setup_packet = (unsigned char *)setup; - - urb.ep->desc.wMaxPacketSize = + memset(urb, 0, sizeof(struct urb)); + memset(hep, 0, sizeof(struct usb_host_endpoint)); + INIT_LIST_HEAD(&hep->urb_list); + INIT_LIST_HEAD(&urb->urb_list); + urb->ep = hep; + urb->complete = musb_host_complete_urb; + urb->status = -EINPROGRESS; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_dma = (unsigned long)buffer; + urb->transfer_buffer_length = len; + urb->setup_packet = (unsigned char *)setup; + + urb->ep->desc.wMaxPacketSize = __cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] : dev->epmaxpacketout[epnum]); - urb.ep->desc.bmAttributes = endpoint_type; - urb.ep->desc.bEndpointAddress = + urb->ep->desc.bmAttributes = endpoint_type; + urb->ep->desc.bEndpointAddress = (is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum; - urb.ep->desc.bInterval = interval; - - return &urb; + urb->ep->desc.bInterval = interval; } -#define MUSB_HOST_TIMEOUT 0x3ffffff - static int submit_urb(struct usb_hcd *hcd, struct urb *urb) { struct musb *host = hcd->hcd_priv; int ret; - int timeout; + unsigned long timeout; ret = musb_urb_enqueue(hcd, urb, 0); if (ret < 0) { @@ -71,12 +73,16 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) return ret; } - timeout = MUSB_HOST_TIMEOUT; + timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe); do { if (ctrlc()) return -EIO; host->isr(0, host); - } while ((urb->dev->status & USB_ST_NOT_PROC) && --timeout); + } while (urb->status == -EINPROGRESS && + get_timer(0) < timeout); + + if (urb->status == -EINPROGRESS) + musb_urb_dequeue(hcd, urb, -ETIME); return urb->status; } @@ -84,38 +90,117 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, struct devrequest *setup) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_CONTROL, pipe, - buffer, len, setup, 0); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, + buffer, len, setup, 0); /* Fix speed for non hub-attached devices */ if (!dev->parent) dev->speed = host_speed; - return submit_urb(&hcd, urb); + return submit_urb(&hcd, &urb); } int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_BULK, pipe, - buffer, len, NULL, 0); - return submit_urb(&hcd, urb); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_BULK, pipe, + buffer, len, NULL, 0); + return submit_urb(&hcd, &urb); } int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_INT, pipe, - buffer, len, NULL, interval); - return submit_urb(&hcd, urb); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_INT, pipe, + buffer, len, NULL, interval); + return submit_urb(&hcd, &urb); } -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, + int queuesize, int elementsize, void *buffer, int interval) +{ + struct int_queue *queue; + int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe); + + if (queuesize != 1) { + printf("ERROR musb int-queues only support queuesize 1\n"); + return NULL; + } + + if (dev->int_pending & (1 << index)) { + printf("ERROR int-urb is already pending on pipe %lx\n", pipe); + return NULL; + } + + queue = malloc(sizeof(*queue)); + if (!queue) + return NULL; + + construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT, + pipe, buffer, elementsize, NULL, interval); + + ret = musb_urb_enqueue(&hcd, &queue->urb, 0); + if (ret < 0) { + printf("Failed to enqueue URB to controller\n"); + free(queue); + return NULL; + } + + dev->int_pending |= 1 << index; + return queue; +} + +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + int index = usb_pipein(queue->urb.pipe) * 16 + + usb_pipeendpoint(queue->urb.pipe); + + if (queue->urb.status == -EINPROGRESS) + musb_urb_dequeue(&hcd, &queue->urb, -ETIME); + + dev->int_pending &= ~(1 << index); + free(queue); + return 0; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) { + if (queue->urb.status != -EINPROGRESS) + return NULL; /* URB has already completed in a prev. poll */ + + host->isr(0, host); + + if (queue->urb.status != -EINPROGRESS) + return queue->urb.transfer_buffer; /* Done */ + + return NULL; /* URB still pending */ +} + +void usb_reset_root_port(void) +{ + void *mbase = host->mregs; u8 power; + + power = musb_readb(mbase, MUSB_POWER); + power &= 0xf0; + musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); + mdelay(50); + power = musb_readb(mbase, MUSB_POWER); + musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); + host->isr(0, host); + host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? + USB_SPEED_HIGH : + (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? + USB_SPEED_FULL : USB_SPEED_LOW; + mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ void *mbase; - int timeout = MUSB_HOST_TIMEOUT; + /* USB spec says it may take up to 1 second for a device to connect */ + unsigned long timeout = get_timer(0) + 1000; if (!host) { printf("MUSB host is not registered\n"); @@ -127,20 +212,11 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) do { if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) break; - } while (--timeout); - if (!timeout) + } while (get_timer(0) < timeout); + if (get_timer(0) >= timeout) return -ENODEV; - power = musb_readb(mbase, MUSB_POWER); - musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); - udelay(30000); - power = musb_readb(mbase, MUSB_POWER); - musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); - host->isr(0, host); - host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? - USB_SPEED_HIGH : - (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? - USB_SPEED_FULL : USB_SPEED_LOW; + usb_reset_root_port(); host->is_active = 1; hcd.hcd_priv = host; diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c new file mode 100644 index 0000000000..778916df00 --- /dev/null +++ b/drivers/usb/musb-new/sunxi.c @@ -0,0 +1,279 @@ +/* + * Allwinner SUNXI "glue layer" + * + * Copyright © 2015 Hans de Goede <hdegoede@redhat.com> + * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi> + * + * Based on the sw_usb "Allwinner OTG Dual Role Controller" code. + * Copyright 2007-2012 (C) Allwinner Technology Co., Ltd. + * javen <javen@allwinnertech.com> + * + * Based on the DA8xx "glue layer" code. + * Copyright (c) 2008-2009 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + */ +#include <common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/usbc.h> +#include "linux-compat.h" +#include "musb_core.h" + +/****************************************************************************** + ****************************************************************************** + * From the Allwinner driver + ****************************************************************************** + ******************************************************************************/ + +/****************************************************************************** + * From include/sunxi_usb_bsp.h + ******************************************************************************/ + +/* reg offsets */ +#define USBC_REG_o_ISCR 0x0400 +#define USBC_REG_o_PHYCTL 0x0404 +#define USBC_REG_o_PHYBIST 0x0408 +#define USBC_REG_o_PHYTUNE 0x040c + +#define USBC_REG_o_VEND0 0x0043 + +/* Interface Status and Control */ +#define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 +#define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 +#define USBC_BP_ISCR_EXT_ID_STATUS 28 +#define USBC_BP_ISCR_EXT_DM_STATUS 27 +#define USBC_BP_ISCR_EXT_DP_STATUS 26 +#define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 +#define USBC_BP_ISCR_MERGED_ID_STATUS 24 + +#define USBC_BP_ISCR_ID_PULLUP_EN 17 +#define USBC_BP_ISCR_DPDM_PULLUP_EN 16 +#define USBC_BP_ISCR_FORCE_ID 14 +#define USBC_BP_ISCR_FORCE_VBUS_VALID 12 +#define USBC_BP_ISCR_VBUS_VALID_SRC 10 + +#define USBC_BP_ISCR_HOSC_EN 7 +#define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 +#define USBC_BP_ISCR_ID_CHANGE_DETECT 5 +#define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 +#define USBC_BP_ISCR_IRQ_ENABLE 3 +#define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 +#define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 +#define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 + +/****************************************************************************** + * From usbc/usbc.c + ******************************************************************************/ + +static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) +{ + u32 temp = reg_val; + + temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); + temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); + temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); + + return temp; +} + +static void USBC_EnableIdPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_DisableIdPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_EnableDpDmPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_DisableDpDmPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceIdToLow(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceIdToHigh(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceVbusValidDisable(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceVbusValidToHigh(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ConfigFIFO_Base(void) +{ + u32 reg_value; + + /* config usb fifo, 8kb mode */ + reg_value = readl(SUNXI_SRAMC_BASE + 0x04); + reg_value &= ~(0x03 << 0); + reg_value |= (1 << 0); + writel(reg_value, SUNXI_SRAMC_BASE + 0x04); +} + +/****************************************************************************** + * MUSB Glue code + ******************************************************************************/ + +static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) +{ + struct musb *musb = __hci; + irqreturn_t retval = IRQ_NONE; + + /* read and flush interrupts */ + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + if (musb->int_usb) + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + if (musb->int_tx) + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + if (musb->int_rx) + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval |= musb_interrupt(musb); + + return retval; +} + +static void sunxi_musb_enable(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + /* select PIO mode */ + musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); + + if (is_host_enabled(musb)) { + /* port power on */ + sunxi_usbc_vbus_enable(0); + } +} + +static void sunxi_musb_disable(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + /* Put the controller back in a pristane state for "usb reset" */ + if (musb->is_active) { + sunxi_usbc_disable(0); + sunxi_usbc_enable(0); + musb->is_active = 0; + } +} + +static int sunxi_musb_init(struct musb *musb) +{ + int err; + + pr_debug("%s():\n", __func__); + + err = sunxi_usbc_request_resources(0); + if (err) + return err; + + musb->isr = sunxi_musb_interrupt; + sunxi_usbc_enable(0); + + USBC_ConfigFIFO_Base(); + USBC_EnableDpDmPullUp(musb->mregs); + USBC_EnableIdPullUp(musb->mregs); + + if (is_host_enabled(musb)) { + /* Host mode */ + USBC_ForceIdToLow(musb->mregs); + USBC_ForceVbusValidToHigh(musb->mregs); + } else { + /* Peripheral mode */ + USBC_ForceIdToHigh(musb->mregs); + USBC_ForceVbusValidDisable(musb->mregs); + } + + return 0; +} + +static int sunxi_musb_exit(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + USBC_DisableDpDmPullUp(musb->mregs); + USBC_DisableIdPullUp(musb->mregs); + sunxi_usbc_vbus_disable(0); + sunxi_usbc_disable(0); + + return sunxi_usbc_free_resources(0); +} + +const struct musb_platform_ops sunxi_musb_ops = { + .init = sunxi_musb_init, + .exit = sunxi_musb_exit, + + .enable = sunxi_musb_enable, + .disable = sunxi_musb_disable, +}; diff --git a/drivers/usb/musb-new/usb-compat.h b/drivers/usb/musb-new/usb-compat.h index 27f656f0ce..50bad378c5 100644 --- a/drivers/usb/musb-new/usb-compat.h +++ b/drivers/usb/musb-new/usb-compat.h @@ -48,6 +48,7 @@ struct urb { list_add_tail(&urb->urb_list, &urb->ep->urb_list); \ ret; }) #define usb_hcd_unlink_urb_from_ep(hcd, urb) list_del_init(&urb->urb_list) +#define usb_hcd_check_unlink_urb(hdc, urb, status) 0 static inline void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, |