diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-04 18:03:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-04 18:03:51 -0700 |
commit | 8f28472a739e8e39adc6e64ee5b460df039f0e4f (patch) | |
tree | 979e35f3d1d2be94c06c942bcdc9ee68cbebaacb /drivers/usb/gadget | |
parent | 4ac4d584886a4f47f8ff3bca0f32ff9a2987d3e5 (diff) | |
parent | c034a43e72dda58e4a184d71f5502ef356e04453 (diff) |
Merge tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH:
"Here is the big USB patchset for 4.12-rc1.
Lots of good stuff here, after many many many attempts, the kernel
finally has a working typeC interface, many thanks to Heikki and
Guenter and others who have taken the time to get this merged. It
wasn't an easy path for them at all.
There's also a staging driver that uses this new api, which is why
it's coming in through this tree.
Along with that, there's the usual huge number of changes for gadget
drivers, xhci, and other stuff. Johan also finally refactored pretty
much every driver that was looking at USB endpoints to do it in a
common way, which will help prevent any "badly-formed" devices from
causing problems in drivers. That too wasn't a simple task.
All of these have been in linux-next for a while with no reported
issues"
* tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (263 commits)
staging: typec: Fairchild FUSB302 Type-c chip driver
staging: typec: Type-C Port Controller Interface driver (tcpci)
staging: typec: USB Type-C Port Manager (tcpm)
usb: host: xhci: remove #ifdef around PM functions
usb: musb: don't mark of_dev_auxdata as initdata
usb: misc: legousbtower: Fix buffers on stack
USB: Revert "cdc-wdm: fix "out-of-sync" due to missing notifications"
usb: Make sure usb/phy/of gets built-in
USB: storage: e-mail update in drivers/usb/storage/unusual_devs.h
usb: host: xhci: print correct command ring address
usb: host: xhci: delete sp_dma_buffers for scratchpad
usb: host: xhci: using correct specification chapter reference for DCBAAP
xhci: switch to pci_alloc_irq_vectors
usb: host: xhci-plat: set resume_quirk() for R-Car controllers
usb: host: xhci-plat: add resume_quirk()
usb: host: xhci-plat: enable clk in resume timing
usb: host: plat: Enable xHCI plat runtime PM
USB: serial: ftdi_sio: add device ID for Microsemi/Arrow SF2PLUS Dev Kit
USB: serial: constify static arrays
usb: fix some references for /proc/bus/usb
...
Diffstat (limited to 'drivers/usb/gadget')
25 files changed, 629 insertions, 416 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8ad203296079..c164d6b788c3 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -212,7 +212,7 @@ config USB_F_TCM # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS - tristate "USB functions configurable through configfs" + tristate "USB Gadget functions configurable through configfs" select USB_LIBCOMPOSITE help A Linux USB "gadget" can be set up through configfs. @@ -458,8 +458,9 @@ config USB_CONFIGFS_F_TCM UAS utilizes the USB 3.0 feature called streams support. choice - tristate "USB Gadget Drivers" + tristate "USB Gadget precomposed configurations" default USB_ETH + optional help A Linux "Gadget Driver" talks to the USB Peripheral Controller driver through the abstract "gadget" API. Some other operating @@ -476,6 +477,12 @@ choice not be able work with that controller, or might need to implement a less common variant of a device class protocol. + The available choices each represent a single precomposed USB + gadget configuration. In the device model, each option contains + both the device instantiation as a child for a USB gadget + controller, and the relevant drivers for each function declared + by the device. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a0085571824d..71dd27c0d7f2 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -246,7 +246,6 @@ EXPORT_SYMBOL_GPL(ffs_lock); static struct ffs_dev *_ffs_find_dev(const char *name); static struct ffs_dev *_ffs_alloc_dev(void); -static int _ffs_name_dev(struct ffs_dev *dev, const char *name); static void _ffs_free_dev(struct ffs_dev *dev); static void *ffs_acquire_dev(const char *dev_name); static void ffs_release_dev(struct ffs_data *ffs_data); @@ -1571,14 +1570,14 @@ static void ffs_data_get(struct ffs_data *ffs) { ENTER(); - atomic_inc(&ffs->ref); + refcount_inc(&ffs->ref); } static void ffs_data_opened(struct ffs_data *ffs) { ENTER(); - atomic_inc(&ffs->ref); + refcount_inc(&ffs->ref); if (atomic_add_return(1, &ffs->opened) == 1 && ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; @@ -1590,7 +1589,7 @@ static void ffs_data_put(struct ffs_data *ffs) { ENTER(); - if (unlikely(atomic_dec_and_test(&ffs->ref))) { + if (unlikely(refcount_dec_and_test(&ffs->ref))) { pr_info("%s(): freeing\n", __func__); ffs_data_clear(ffs); BUG_ON(waitqueue_active(&ffs->ev.waitq) || @@ -1635,7 +1634,7 @@ static struct ffs_data *ffs_data_new(void) ENTER(); - atomic_set(&ffs->ref, 1); + refcount_set(&ffs->ref, 1); atomic_set(&ffs->opened, 0); ffs->state = FFS_READ_DESCRIPTORS; mutex_init(&ffs->mutex); @@ -3302,9 +3301,10 @@ static struct ffs_dev *_ffs_do_find_dev(const char *name) { struct ffs_dev *dev; + if (!name) + return NULL; + list_for_each_entry(dev, &ffs_devices, entry) { - if (!dev->name || !name) - continue; if (strcmp(dev->name, name) == 0) return dev; } @@ -3380,42 +3380,11 @@ static void ffs_free_inst(struct usb_function_instance *f) kfree(opts); } -#define MAX_INST_NAME_LEN 40 - static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name) { - struct f_fs_opts *opts; - char *ptr; - const char *tmp; - int name_len, ret; - - name_len = strlen(name) + 1; - if (name_len > MAX_INST_NAME_LEN) + if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name)) return -ENAMETOOLONG; - - ptr = kstrndup(name, name_len, GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - opts = to_f_fs_opts(fi); - tmp = NULL; - - ffs_dev_lock(); - - tmp = opts->dev->name_allocated ? opts->dev->name : NULL; - ret = _ffs_name_dev(opts->dev, ptr); - if (ret) { - kfree(ptr); - ffs_dev_unlock(); - return ret; - } - opts->dev->name_allocated = true; - - ffs_dev_unlock(); - - kfree(tmp); - - return 0; + return ffs_name_dev(to_f_fs_opts(fi)->dev, name); } static struct usb_function_instance *ffs_alloc_inst(void) @@ -3545,32 +3514,19 @@ static struct ffs_dev *_ffs_alloc_dev(void) return dev; } -/* - * ffs_lock must be taken by the caller of this function - * The caller is responsible for "name" being available whenever f_fs needs it - */ -static int _ffs_name_dev(struct ffs_dev *dev, const char *name) +int ffs_name_dev(struct ffs_dev *dev, const char *name) { struct ffs_dev *existing; + int ret = 0; - existing = _ffs_do_find_dev(name); - if (existing) - return -EBUSY; - - dev->name = name; - - return 0; -} + ffs_dev_lock(); -/* - * The caller is responsible for "name" being available whenever f_fs needs it - */ -int ffs_name_dev(struct ffs_dev *dev, const char *name) -{ - int ret; + existing = _ffs_do_find_dev(name); + if (!existing) + strlcpy(dev->name, name, ARRAY_SIZE(dev->name)); + else if (existing != dev) + ret = -EBUSY; - ffs_dev_lock(); - ret = _ffs_name_dev(dev, name); ffs_dev_unlock(); return ret; @@ -3600,8 +3556,6 @@ EXPORT_SYMBOL_GPL(ffs_single_dev); static void _ffs_free_dev(struct ffs_dev *dev) { list_del(&dev->entry); - if (dev->name_allocated) - kfree(dev->name); /* Clear the private_data pointer to stop incorrect dev access */ if (dev->ffs_data) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index c3cab77181d4..a8b40d07e927 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -178,6 +178,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req); static int rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) { + struct usb_gadget *g = dev->gadget; struct sk_buff *skb; int retval = -ENOMEM; size_t size = 0; @@ -209,8 +210,11 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) */ size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA; size += dev->port_usb->header_len; - size += out->maxpacket - 1; - size -= size % out->maxpacket; + + if (g->quirk_ep_out_aligned_size) { + size += out->maxpacket - 1; + size -= size % out->maxpacket; + } if (dev->port_usb->is_fixed) size = max_t(size_t, size, dev->port_usb->fixed_out_len); @@ -401,13 +405,12 @@ done: static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) { struct usb_request *req; + struct usb_request *tmp; unsigned long flags; /* fill unused rxq slots with some skb */ spin_lock_irqsave(&dev->req_lock, flags); - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); + list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) { list_del_init(&req->list); spin_unlock_irqrestore(&dev->req_lock, flags); @@ -527,7 +530,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, return NETDEV_TX_BUSY; } - req = container_of(dev->tx_reqs.next, struct usb_request, list); + req = list_first_entry(&dev->tx_reqs, struct usb_request, list); list_del(&req->list); /* temporarily stop TX queue when the freelist empties */ @@ -1122,6 +1125,7 @@ void gether_disconnect(struct gether *link) { struct eth_dev *dev = link->ioport; struct usb_request *req; + struct usb_request *tmp; WARN_ON(!dev); if (!dev) @@ -1138,9 +1142,7 @@ void gether_disconnect(struct gether *link) */ usb_ep_disable(link->in_ep); spin_lock(&dev->req_lock); - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, - struct usb_request, list); + list_for_each_entry_safe(req, tmp, &dev->tx_reqs, list) { list_del(&req->list); spin_unlock(&dev->req_lock); @@ -1152,9 +1154,7 @@ void gether_disconnect(struct gether *link) usb_ep_disable(link->out_ep); spin_lock(&dev->req_lock); - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); + list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) { list_del(&req->list); spin_unlock(&dev->req_lock); diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index 4b6969451cdc..4378cc2fcac3 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -20,6 +20,7 @@ #include <linux/list.h> #include <linux/mutex.h> #include <linux/workqueue.h> +#include <linux/refcount.h> #ifdef VERBOSE_DEBUG #ifndef pr_vdebug @@ -39,15 +40,16 @@ struct f_fs_opts; struct ffs_dev { - const char *name; - bool name_allocated; - bool mounted; - bool desc_ready; - bool single; struct ffs_data *ffs_data; struct f_fs_opts *opts; struct list_head entry; + char name[41]; + + bool mounted; + bool desc_ready; + bool single; + int (*ffs_ready_callback)(struct ffs_data *ffs); void (*ffs_closed_callback)(struct ffs_data *ffs); void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev); @@ -177,7 +179,7 @@ struct ffs_data { struct completion ep0req_completion; /* P: mutex */ /* reference counter */ - atomic_t ref; + refcount_t ref; /* how many files are opened (EP0 and others) */ atomic_t opened; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 4e037d2a7a60..844cb738bafd 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -2125,7 +2125,7 @@ static struct configfs_item_operations uvc_item_ops = { .release = uvc_attr_release, }; -#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \ +#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ static ssize_t f_uvc_opts_##cname##_show( \ struct config_item *item, char *page) \ { \ @@ -2168,16 +2168,16 @@ end: \ return ret; \ } \ \ -UVC_ATTR(f_uvc_opts_, cname, aname) +UVC_ATTR(f_uvc_opts_, cname, cname) #define identity_conv(x) (x) -UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv, - 16); -UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu, - 3072); -UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv, - 15); +UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv, + kstrtou8, u8, identity_conv, 16); +UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu, + kstrtou16, u16, le16_to_cpu, 3072); +UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv, + kstrtou8, u8, identity_conv, 15); #undef identity_conv diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index a2c916869293..b9ca0a26cbd9 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -27,6 +27,7 @@ #include <linux/mmu_context.h> #include <linux/aio.h> #include <linux/uio.h> +#include <linux/refcount.h> #include <linux/device.h> #include <linux/moduleparam.h> @@ -114,7 +115,7 @@ enum ep0_state { struct dev_data { spinlock_t lock; - atomic_t count; + refcount_t count; enum ep0_state state; /* P: lock */ struct usb_gadgetfs_event event [N_EVENT]; unsigned ev_next; @@ -150,12 +151,12 @@ struct dev_data { static inline void get_dev (struct dev_data *data) { - atomic_inc (&data->count); + refcount_inc (&data->count); } static void put_dev (struct dev_data *data) { - if (likely (!atomic_dec_and_test (&data->count))) + if (likely (!refcount_dec_and_test (&data->count))) return; /* needs no more cleanup */ BUG_ON (waitqueue_active (&data->wait)); @@ -170,7 +171,7 @@ static struct dev_data *dev_new (void) if (!dev) return NULL; dev->state = STATE_DEV_DISABLED; - atomic_set (&dev->count, 1); + refcount_set (&dev->count, 1); spin_lock_init (&dev->lock); INIT_LIST_HEAD (&dev->epfiles); init_waitqueue_head (&dev->wait); @@ -190,7 +191,7 @@ enum ep_state { struct ep_data { struct mutex lock; enum ep_state state; - atomic_t count; + refcount_t count; struct dev_data *dev; /* must hold dev->lock before accessing ep or req */ struct usb_ep *ep; @@ -205,12 +206,12 @@ struct ep_data { static inline void get_ep (struct ep_data *data) { - atomic_inc (&data->count); + refcount_inc (&data->count); } static void put_ep (struct ep_data *data) { - if (likely (!atomic_dec_and_test (&data->count))) + if (likely (!refcount_dec_and_test (&data->count))) return; put_dev (data->dev); /* needs no more cleanup */ @@ -1561,7 +1562,7 @@ static int activate_ep_files (struct dev_data *dev) init_waitqueue_head (&data->wait); strncpy (data->name, ep->name, sizeof (data->name) - 1); - atomic_set (&data->count, 1); + refcount_set (&data->count, 1); data->dev = dev; get_dev (dev); diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 4b69f28a9af9..1c14c283cc47 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -62,8 +62,9 @@ config USB_ATMEL_USBA The fifo_mode parameter is used to select endpoint allocation mode. fifo_mode = 0 is used to let the driver autoconfigure the endpoints. - In this case 2 banks are allocated for isochronous endpoints and - only one bank is allocated for the rest of the endpoints. + In this case, for ep1 2 banks are allocated if it works in isochronous + mode and only 1 bank otherwise. For the rest of the endpoints + only 1 bank is allocated. fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration allowing the usage of ep1 - ep6 @@ -191,6 +192,7 @@ config USB_RENESAS_USBHS_UDC config USB_RENESAS_USB3 tristate 'Renesas USB3.0 Peripheral controller' depends on ARCH_RENESAS || COMPILE_TEST + depends on EXTCON help Renesas USB3.0 Peripheral controller is a USB peripheral controller that supports super, high, and full speed USB 3.0 data transfers. @@ -253,6 +255,20 @@ config USB_MV_U3D MARVELL PXA2128 Processor series include a super speed USB3.0 device controller, which support super speed USB peripheral. +config USB_SNP_CORE + depends on USB_AMD5536UDC + tristate + help + This enables core driver support for Synopsys USB 2.0 Device + controller. + + This will be enabled when PCI or Platform driver for this UDC is + selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or + USB_AMD5536UDC options. + + This IP is different to the High Speed OTG IP that can be enabled + by selecting USB_DWC2 or USB_DWC3 options. + # # Controllers available in both integrated and discrete versions # @@ -277,7 +293,8 @@ source "drivers/usb/gadget/udc/bdc/Kconfig" config USB_AMD5536UDC tristate "AMD5536 UDC" - depends on PCI + depends on USB_PCI + select USB_SNP_CORE help The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. It is a USB Highspeed DMA capable USB device controller. Beside ep0 @@ -285,6 +302,9 @@ config USB_AMD5536UDC The UDC port supports OTG operation, and may be used as a host port if it's not being used to implement peripheral or OTG roles. + This UDC is based on Synopsys USB device controller IP and selects + CONFIG_USB_SNP_CORE option to build the core driver. + Say "y" to link the driver statically, or "m" to build a dynamically linked module called "amd5536udc" and force all gadget drivers to also be dynamically linked. @@ -327,7 +347,7 @@ config USB_NET2272_DMA config USB_NET2280 tristate "NetChip NET228x / PLX USB3x8x" - depends on PCI + depends on USB_PCI help NetChip 2280 / 2282 is a PCI based USB peripheral controller which supports both full and high speed USB 2.0 data transfers. @@ -352,7 +372,7 @@ config USB_NET2280 config USB_GOKU tristate "Toshiba TC86C001 'Goku-S'" - depends on PCI + depends on USB_PCI help The Toshiba TC86C001 is a PCI device which includes controllers for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). @@ -366,7 +386,7 @@ config USB_GOKU config USB_EG20T tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" - depends on PCI + depends on USB_PCI help This is a USB device driver for EG20T PCH. EG20T PCH is the platform controller hub that is used in Intel's diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 98e74ed9f555..626e1f1c62da 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -10,7 +10,8 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o -obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o +obj-$(CONFIG_USB_SNP_CORE) += amd5536udc.o +obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index ea03ca7ae29a..4ecd2f20ea48 100644 --- a/drivers/usb/gadget/udc/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c @@ -11,27 +11,15 @@ */ /* - * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536. - * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it - * provides 4 IN and 4 OUT endpoints (bulk or interrupt type). - * - * Make sure that UDC is assigned to port 4 by BIOS settings (port can also - * be used as host port) and UOC bits PAD_EN and APU are set (should be done - * by BIOS init). - * - * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not - * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0") - * can be used with gadget ether. + * This file does the core driver implementation for the UDC that is based + * on Synopsys device controller IP (different than HS OTG IP) that is either + * connected through PCI bus or integrated to SoC platforms. */ -/* debug control */ -/* #define UDC_VERBOSE */ - /* Driver strings */ -#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller" +#define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller" #define UDC_DRIVER_VERSION_STRING "01.00.0206" -/* system */ #include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> @@ -46,23 +34,12 @@ #include <linux/ioctl.h> #include <linux/fs.h> #include <linux/dmapool.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/irq.h> #include <linux/prefetch.h> - +#include <linux/moduleparam.h> #include <asm/byteorder.h> #include <asm/unaligned.h> - -/* gadget stack */ -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - -/* udc specific */ #include "amd5536udc.h" - static void udc_tasklet_disconnect(unsigned long); static void empty_req_queue(struct udc_ep *); static void udc_setup_endpoints(struct udc *dev); @@ -72,7 +49,7 @@ static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq); /* description */ static const char mod_desc[] = UDC_MOD_DESCRIPTION; -static const char name[] = "amd5536udc"; +static const char name[] = "udc"; /* structure to hold endpoint function pointers */ static const struct usb_ep_ops udc_ep_ops; @@ -208,30 +185,11 @@ static const struct { #undef EP_INFO }; -/* DMA usage flag */ -static bool use_dma = 1; -/* packet per buffer dma */ -static bool use_dma_ppb = 1; -/* with per descr. update */ -static bool use_dma_ppb_du; /* buffer fill mode */ static int use_dma_bufferfill_mode; -/* full speed only mode */ -static bool use_fullspeed; /* tx buffer size for high speed */ static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE; -/* module parameters */ -module_param(use_dma, bool, S_IRUGO); -MODULE_PARM_DESC(use_dma, "true for DMA"); -module_param(use_dma_ppb, bool, S_IRUGO); -MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode"); -module_param(use_dma_ppb_du, bool, S_IRUGO); -MODULE_PARM_DESC(use_dma_ppb_du, - "true for DMA in packet per buffer mode with descriptor update"); -module_param(use_fullspeed, bool, S_IRUGO); -MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only"); - /*---------------------------------------------------------------------------*/ /* Prints UDC device registers and endpoint irq registers */ static void print_regs(struct udc *dev) @@ -267,7 +225,7 @@ static void print_regs(struct udc *dev) } /* Masks unused interrupts */ -static int udc_mask_unused_interrupts(struct udc *dev) +int udc_mask_unused_interrupts(struct udc *dev) { u32 tmp; @@ -287,6 +245,7 @@ static int udc_mask_unused_interrupts(struct udc *dev) return 0; } +EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts); /* Enables endpoint 0 interrupts */ static int udc_enable_ep0_interrupts(struct udc *dev) @@ -306,7 +265,7 @@ static int udc_enable_ep0_interrupts(struct udc *dev) } /* Enables device interrupts for SET_INTF and SET_CONFIG */ -static int udc_enable_dev_setup_interrupts(struct udc *dev) +int udc_enable_dev_setup_interrupts(struct udc *dev) { u32 tmp; @@ -325,6 +284,7 @@ static int udc_enable_dev_setup_interrupts(struct udc *dev) return 0; } +EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts); /* Calculates fifo start of endpoint based on preceding endpoints */ static int udc_set_txfifo_addr(struct udc_ep *ep) @@ -583,7 +543,7 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp) if (ep->dma) { /* ep0 in requests are allocated from data pool here */ - dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp, + dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp, &req->td_phys); if (!dma_desc) { kfree(req); @@ -608,27 +568,23 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp) } /* frees pci pool descriptors of a DMA chain */ -static int udc_free_dma_chain(struct udc *dev, struct udc_request *req) +static void udc_free_dma_chain(struct udc *dev, struct udc_request *req) { - int ret_val = 0; - struct udc_data_dma *td; - struct udc_data_dma *td_last = NULL; + struct udc_data_dma *td = req->td_data; unsigned int i; + dma_addr_t addr_next = 0x00; + dma_addr_t addr = (dma_addr_t)td->next; + DBG(dev, "free chain req = %p\n", req); /* do not free first desc., will be done by free for request */ - td_last = req->td_data; - td = phys_to_virt(td_last->next); - for (i = 1; i < req->chain_len; i++) { - pci_pool_free(dev->data_requests, td, - (dma_addr_t)td_last->next); - td_last = td; - td = phys_to_virt(td_last->next); + td = phys_to_virt(addr); + addr_next = (dma_addr_t)td->next; + dma_pool_free(dev->data_requests, td, addr); + addr = addr_next; } - - return ret_val; } /* Frees request packet, called by gadget driver */ @@ -652,7 +608,7 @@ udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq) if (req->chain_len > 1) udc_free_dma_chain(ep->dev, req); - pci_pool_free(ep->dev->data_requests, req->td_data, + dma_pool_free(ep->dev->data_requests, req->td_data, req->td_phys); } kfree(req); @@ -847,7 +803,7 @@ static int udc_create_dma_chain( for (i = buf_len; i < bytes; i += buf_len) { /* create or determine next desc. */ if (create_new_chain) { - td = pci_pool_alloc(ep->dev->data_requests, + td = dma_pool_alloc(ep->dev->data_requests, gfp_flags, &dma_addr); if (!td) return -ENOMEM; @@ -1507,7 +1463,7 @@ static void make_ep_lists(struct udc *dev) } /* Inits UDC context */ -static void udc_basic_init(struct udc *dev) +void udc_basic_init(struct udc *dev) { u32 tmp; @@ -1543,6 +1499,7 @@ static void udc_basic_init(struct udc *dev) dev->data_ep_enabled = 0; dev->data_ep_queued = 0; } +EXPORT_SYMBOL_GPL(udc_basic_init); /* init registers at driver load time */ static int startup_registers(struct udc *dev) @@ -3031,7 +2988,7 @@ __acquires(dev->lock) } /* Interrupt Service Routine, see Linux Kernel Doc for parameters */ -static irqreturn_t udc_irq(int irq, void *pdev) +irqreturn_t udc_irq(int irq, void *pdev) { struct udc *dev = pdev; u32 reg; @@ -3083,16 +3040,18 @@ static irqreturn_t udc_irq(int irq, void *pdev) spin_unlock(&dev->lock); return ret_val; } +EXPORT_SYMBOL_GPL(udc_irq); /* Tears down device */ -static void gadget_release(struct device *pdev) +void gadget_release(struct device *pdev) { struct amd5536udc *dev = dev_get_drvdata(pdev); kfree(dev); } +EXPORT_SYMBOL_GPL(gadget_release); /* Cleanup on device remove */ -static void udc_remove(struct udc *dev) +void udc_remove(struct udc *dev) { /* remove timer */ stop_timer++; @@ -3108,9 +3067,10 @@ static void udc_remove(struct udc *dev) del_timer_sync(&udc_pollstall_timer); udc = NULL; } +EXPORT_SYMBOL_GPL(udc_remove); /* free all the dma pools */ -static void free_dma_pools(struct udc *dev) +void free_dma_pools(struct udc *dev) { dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td, dev->ep[UDC_EP0OUT_IX].td_phys); @@ -3119,35 +3079,10 @@ static void free_dma_pools(struct udc *dev) dma_pool_destroy(dev->stp_requests); dma_pool_destroy(dev->data_requests); } - -/* Reset all pci context */ -static void udc_pci_remove(struct pci_dev *pdev) -{ - struct udc *dev; - - dev = pci_get_drvdata(pdev); - - usb_del_gadget_udc(&udc->gadget); - /* gadget driver must not be registered */ - if (WARN_ON(dev->driver)) - return; - - /* dma pool cleanup */ - free_dma_pools(dev); - - /* reset controller */ - writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); - free_irq(pdev->irq, dev); - iounmap(dev->virt_addr); - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - pci_disable_device(pdev); - - udc_remove(dev); -} +EXPORT_SYMBOL_GPL(free_dma_pools); /* create dma pools on init */ -static int init_dma_pools(struct udc *dev) +int init_dma_pools(struct udc *dev) { struct udc_stp_dma *td_stp; struct udc_data_dma *td_data; @@ -3210,9 +3145,10 @@ err_create_dma_pool: dev->data_requests = NULL; return retval; } +EXPORT_SYMBOL_GPL(init_dma_pools); /* general probe */ -static int udc_probe(struct udc *dev) +int udc_probe(struct udc *dev) { char tmp[128]; u32 reg; @@ -3276,137 +3212,7 @@ static int udc_probe(struct udc *dev) finished: return retval; } - -/* Called by pci bus driver to init pci context */ -static int udc_pci_probe( - struct pci_dev *pdev, - const struct pci_device_id *id -) -{ - struct udc *dev; - unsigned long resource; - unsigned long len; - int retval = 0; - - /* one udc only */ - if (udc) { - dev_dbg(&pdev->dev, "already probed\n"); - return -EBUSY; - } - - /* init */ - dev = kzalloc(sizeof(struct udc), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - /* pci setup */ - if (pci_enable_device(pdev) < 0) { - retval = -ENODEV; - goto err_pcidev; - } - - /* PCI resource allocation */ - resource = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - - if (!request_mem_region(resource, len, name)) { - dev_dbg(&pdev->dev, "pci device used already\n"); - retval = -EBUSY; - goto err_memreg; - } - - dev->virt_addr = ioremap_nocache(resource, len); - if (!dev->virt_addr) { - dev_dbg(&pdev->dev, "start address cannot be mapped\n"); - retval = -EFAULT; - goto err_ioremap; - } - - if (!pdev->irq) { - dev_err(&pdev->dev, "irq not set\n"); - retval = -ENODEV; - goto err_irq; - } - - spin_lock_init(&dev->lock); - /* udc csr registers base */ - dev->csr = dev->virt_addr + UDC_CSR_ADDR; - /* dev registers base */ - dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; - /* ep registers base */ - dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; - /* fifo's base */ - dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); - dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); - - if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { - dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq); - retval = -EBUSY; - goto err_irq; - } - - pci_set_drvdata(pdev, dev); - - /* chip revision for Hs AMD5536 */ - dev->chiprev = pdev->revision; - - pci_set_master(pdev); - pci_try_set_mwi(pdev); - - /* init dma pools */ - if (use_dma) { - retval = init_dma_pools(dev); - if (retval != 0) - goto err_dma; - } - - dev->phys_addr = resource; - dev->irq = pdev->irq; - dev->pdev = pdev; - - /* general probing */ - if (udc_probe(dev)) { - retval = -ENODEV; - goto err_probe; - } - return 0; - -err_probe: - if (use_dma) - free_dma_pools(dev); -err_dma: - free_irq(pdev->irq, dev); -err_irq: - iounmap(dev->virt_addr); -err_ioremap: - release_mem_region(resource, len); -err_memreg: - pci_disable_device(pdev); -err_pcidev: - kfree(dev); - return retval; -} - -/* PCI device parameters */ -static const struct pci_device_id pci_id[] = { - { - PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096), - .class = PCI_CLASS_SERIAL_USB_DEVICE, - .class_mask = 0xffffffff, - }, - {}, -}; -MODULE_DEVICE_TABLE(pci, pci_id); - -/* PCI functions */ -static struct pci_driver udc_pci_driver = { - .name = (char *) name, - .id_table = pci_id, - .probe = udc_pci_probe, - .remove = udc_pci_remove, -}; - -module_pci_driver(udc_pci_driver); +EXPORT_SYMBOL_GPL(udc_probe); MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); MODULE_AUTHOR("Thomas Dahlmann"); diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 4638d707f169..fae49bf3833e 100644 --- a/drivers/usb/gadget/udc/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h @@ -13,6 +13,12 @@ #ifndef AMD5536UDC_H #define AMD5536UDC_H +/* debug control */ +/* #define UDC_VERBOSE */ + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + /* various constants */ #define UDC_RDE_TIMER_SECONDS 1 #define UDC_RDE_TIMER_DIV 10 @@ -545,8 +551,8 @@ struct udc { u32 __iomem *txfifo; /* DMA desc pools */ - struct pci_pool *data_requests; - struct pci_pool *stp_requests; + struct dma_pool *data_requests; + struct dma_pool *stp_requests; /* device data */ unsigned long phys_addr; @@ -567,6 +573,36 @@ union udc_setup_data { struct usb_ctrlrequest request; }; +/* Function declarations */ +int udc_enable_dev_setup_interrupts(struct udc *dev); +int udc_mask_unused_interrupts(struct udc *dev); +irqreturn_t udc_irq(int irq, void *pdev); +void gadget_release(struct device *pdev); +void udc_basic_init(struct udc *dev); +void free_dma_pools(struct udc *dev); +int init_dma_pools(struct udc *dev); +void udc_remove(struct udc *dev); +int udc_probe(struct udc *dev); + +/* DMA usage flag */ +static bool use_dma = 1; +/* packet per buffer dma */ +static bool use_dma_ppb = 1; +/* with per descr. update */ +static bool use_dma_ppb_du; +/* full speed only mode */ +static bool use_fullspeed; + +/* module parameters */ +module_param(use_dma, bool, S_IRUGO); +MODULE_PARM_DESC(use_dma, "true for DMA"); +module_param(use_dma_ppb, bool, S_IRUGO); +MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode"); +module_param(use_dma_ppb_du, bool, S_IRUGO); +MODULE_PARM_DESC(use_dma_ppb_du, + "true for DMA in packet per buffer mode with descriptor update"); +module_param(use_fullspeed, bool, S_IRUGO); +MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only"); /* *--------------------------------------------------------------------------- * SET and GET bitfields in u32 values diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c new file mode 100644 index 000000000000..2a2d0a96fe24 --- /dev/null +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -0,0 +1,217 @@ +/* + * amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller + * + * Copyright (C) 2005-2007 AMD (http://www.amd.com) + * Author: Thomas Dahlmann + * + * 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. + */ + +/* + * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536. + * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it + * provides 4 IN and 4 OUT endpoints (bulk or interrupt type). + * + * Make sure that UDC is assigned to port 4 by BIOS settings (port can also + * be used as host port) and UOC bits PAD_EN and APU are set (should be done + * by BIOS init). + * + * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not + * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0") + * can be used with gadget ether. + * + * This file does pci device registration, and the core driver implementation + * is done in amd5536udc.c + * + * The driver is split so as to use the core UDC driver which is based on + * Synopsys device controller IP (different than HS OTG IP) in UDCs + * integrated to SoC platforms. + * + */ + +/* Driver strings */ +#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller" + +/* system */ +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/prefetch.h> +#include <linux/pci.h> + +/* udc specific */ +#include "amd5536udc.h" + +/* pointer to device object */ +static struct udc *udc; + +/* description */ +static const char mod_desc[] = UDC_MOD_DESCRIPTION; +static const char name[] = "amd5536udc-pci"; + +/* Reset all pci context */ +static void udc_pci_remove(struct pci_dev *pdev) +{ + struct udc *dev; + + dev = pci_get_drvdata(pdev); + + usb_del_gadget_udc(&udc->gadget); + /* gadget driver must not be registered */ + if (WARN_ON(dev->driver)) + return; + + /* dma pool cleanup */ + free_dma_pools(dev); + + /* reset controller */ + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + free_irq(pdev->irq, dev); + iounmap(dev->virt_addr); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); + + udc_remove(dev); +} + +/* Called by pci bus driver to init pci context */ +static int udc_pci_probe( + struct pci_dev *pdev, + const struct pci_device_id *id +) +{ + struct udc *dev; + unsigned long resource; + unsigned long len; + int retval = 0; + + /* one udc only */ + if (udc) { + dev_dbg(&pdev->dev, "already probed\n"); + return -EBUSY; + } + + /* init */ + dev = kzalloc(sizeof(struct udc), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* pci setup */ + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto err_pcidev; + } + + /* PCI resource allocation */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + + if (!request_mem_region(resource, len, name)) { + dev_dbg(&pdev->dev, "pci device used already\n"); + retval = -EBUSY; + goto err_memreg; + } + + dev->virt_addr = ioremap_nocache(resource, len); + if (!dev->virt_addr) { + dev_dbg(&pdev->dev, "start address cannot be mapped\n"); + retval = -EFAULT; + goto err_ioremap; + } + + if (!pdev->irq) { + dev_err(&pdev->dev, "irq not set\n"); + retval = -ENODEV; + goto err_irq; + } + + spin_lock_init(&dev->lock); + /* udc csr registers base */ + dev->csr = dev->virt_addr + UDC_CSR_ADDR; + /* dev registers base */ + dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; + /* ep registers base */ + dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; + /* fifo's base */ + dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); + dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); + + if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { + dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq); + retval = -EBUSY; + goto err_irq; + } + + pci_set_drvdata(pdev, dev); + + /* chip revision for Hs AMD5536 */ + dev->chiprev = pdev->revision; + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + /* init dma pools */ + if (use_dma) { + retval = init_dma_pools(dev); + if (retval != 0) + goto err_dma; + } + + dev->phys_addr = resource; + dev->irq = pdev->irq; + dev->pdev = pdev; + + /* general probing */ + if (udc_probe(dev)) { + retval = -ENODEV; + goto err_probe; + } + return 0; + +err_probe: + if (use_dma) + free_dma_pools(dev); +err_dma: + free_irq(pdev->irq, dev); +err_irq: + iounmap(dev->virt_addr); +err_ioremap: + release_mem_region(resource, len); +err_memreg: + pci_disable_device(pdev); +err_pcidev: + kfree(dev); + return retval; +} + +/* PCI device parameters */ +static const struct pci_device_id pci_id[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096), + .class = PCI_CLASS_SERIAL_USB_DEVICE, + .class_mask = 0xffffffff, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, pci_id); + +/* PCI functions */ +static struct pci_driver udc_pci_driver = { + .name = (char *) name, + .id_table = pci_id, + .probe = udc_pci_probe, + .remove = udc_pci_remove, +}; +module_pci_driver(udc_pci_driver); + +MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); +MODULE_AUTHOR("Thomas Dahlmann"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 2035906b8ced..3ccc34176a5a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -321,7 +321,6 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) static ushort fifo_mode; -/* "modprobe ... fifo_mode=1" etc */ module_param(fifo_mode, ushort, 0x0); MODULE_PARM_DESC(fifo_mode, "Endpoint configuration mode"); @@ -371,7 +370,7 @@ static struct usba_fifo_cfg mode_4_cfg[] = { }; /* Add additional configurations here */ -int usba_config_fifo_table(struct usba_udc *udc) +static int usba_config_fifo_table(struct usba_udc *udc) { int n; @@ -1076,11 +1075,9 @@ static int atmel_usba_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver); static int atmel_usba_stop(struct usb_gadget *gadget); -static struct usb_ep *atmel_usba_match_ep( - struct usb_gadget *gadget, - struct usb_endpoint_descriptor *desc, - struct usb_ss_ep_comp_descriptor *ep_comp -) +static struct usb_ep *atmel_usba_match_ep(struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp) { struct usb_ep *_ep; struct usba_ep *ep; @@ -1100,7 +1097,6 @@ found_ep: ep = to_usba_ep(_ep); switch (usb_endpoint_type(desc)) { - case USB_ENDPOINT_XFER_CONTROL: break; @@ -1141,7 +1137,7 @@ found_ep: ep->udc->configured_ep++; } -return _ep; + return _ep; } static const struct usb_gadget_ops usba_udc_ops = { @@ -1855,8 +1851,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) * but it's clearly harmless... */ if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) - dev_dbg(&udc->pdev->dev, - "ODD: EP0 configuration is invalid!\n"); + dev_err(&udc->pdev->dev, + "ODD: EP0 configuration is invalid!\n"); /* Preallocate other endpoints */ n = fifo_mode ? udc->num_ep : udc->configured_ep; @@ -1864,8 +1860,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) ep = &udc->usba_ep[i]; usba_ep_writel(ep, CFG, ep->ept_cfg); if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) - dev_dbg(&udc->pdev->dev, - "ODD: EP%d configuration is invalid!\n", i); + dev_err(&udc->pdev->dev, + "ODD: EP%d configuration is invalid!\n", i); } } @@ -2089,8 +2085,9 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, while ((pp = of_get_next_child(np, pp))) udc->num_ep++; udc->configured_ep = 1; - } else + } else { udc->num_ep = usba_config_fifo_table(udc); + } eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep, GFP_KERNEL); @@ -2118,14 +2115,34 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret); goto err; } - ep->fifo_size = fifo_mode ? udc->fifo_cfg[i].fifo_size : val; + if (fifo_mode) { + if (val < udc->fifo_cfg[i].fifo_size) { + dev_warn(&pdev->dev, + "Using max fifo-size value from DT\n"); + ep->fifo_size = val; + } else { + ep->fifo_size = udc->fifo_cfg[i].fifo_size; + } + } else { + ep->fifo_size = val; + } ret = of_property_read_u32(pp, "atmel,nb-banks", &val); if (ret) { dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret); goto err; } - ep->nr_banks = fifo_mode ? udc->fifo_cfg[i].nr_banks : val; + if (fifo_mode) { + if (val < udc->fifo_cfg[i].nr_banks) { + dev_warn(&pdev->dev, + "Using max nb-banks value from DT\n"); + ep->nr_banks = val; + } else { + ep->nr_banks = udc->fifo_cfg[i].nr_banks; + } + } else { + ep->nr_banks = val; + } ep->can_dma = of_property_read_bool(pp, "atmel,can-dma"); ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc"); diff --git a/drivers/usb/gadget/udc/bdc/Kconfig b/drivers/usb/gadget/udc/bdc/Kconfig index 0d7b8c9f72fd..eb8b55392360 100644 --- a/drivers/usb/gadget/udc/bdc/Kconfig +++ b/drivers/usb/gadget/udc/bdc/Kconfig @@ -14,7 +14,7 @@ if USB_BDC_UDC comment "Platform Support" config USB_BDC_PCI tristate "BDC support for PCIe based platforms" - depends on PCI + depends on USB_PCI default USB_BDC_UDC help Enable support for platforms which have BDC connected through PCIe, such as Lego3 FPGA platform. diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d685d82dcf48..efce68e9a8e0 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1273,6 +1273,7 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) flush_work(&gadget->work); device_unregister(&udc->dev); device_unregister(&gadget->dev); + memset(&gadget->dev, 0x00, sizeof(gadget->dev)); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 8cabc5944d5f..c79081952ea0 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2062,16 +2062,13 @@ static int dummy_hub_control( } break; case USB_PORT_FEAT_POWER: - if (hcd->speed == HCD_USB3) { - if (dum_hcd->port_status & USB_PORT_STAT_POWER) - dev_dbg(dummy_dev(dum_hcd), - "power-off\n"); - } else - if (dum_hcd->port_status & - USB_SS_PORT_STAT_POWER) - dev_dbg(dummy_dev(dum_hcd), - "power-off\n"); - /* FALLS THROUGH */ + dev_dbg(dummy_dev(dum_hcd), "power-off\n"); + if (hcd->speed == HCD_USB3) + dum_hcd->port_status &= ~USB_SS_PORT_STAT_POWER; + else + dum_hcd->port_status &= ~USB_PORT_STAT_POWER; + set_link_state(dum_hcd); + break; default: dum_hcd->port_status &= ~(1 << wValue); set_link_state(dum_hcd); @@ -2242,14 +2239,13 @@ static int dummy_hub_control( if ((dum_hcd->port_status & USB_SS_PORT_STAT_POWER) != 0) { dum_hcd->port_status |= (1 << wValue); - set_link_state(dum_hcd); } } else if ((dum_hcd->port_status & USB_PORT_STAT_POWER) != 0) { dum_hcd->port_status |= (1 << wValue); - set_link_state(dum_hcd); } + set_link_state(dum_hcd); } break; case GetPortErrorCount: diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index b76fcdb763a0..6f2f71c054be 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2676,6 +2676,8 @@ static const struct platform_device_id fsl_udc_devtype[] = { }, { .name = "imx-udc-mx51", }, { + .name = "fsl-usb2-udc", + }, { /* sentinel */ } }; diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index d365449a295a..772049afe166 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -1835,13 +1835,18 @@ static int mv_u3d_probe(struct platform_device *dev) } /* we will access controller register, so enable the u3d controller */ - clk_enable(u3d->clk); + retval = clk_enable(u3d->clk); + if (retval) { + dev_err(&dev->dev, "clk_enable error %d\n", retval); + goto err_u3d_enable; + } if (pdata->phy_init) { retval = pdata->phy_init(u3d->phy_regs); if (retval) { dev_err(&dev->dev, "init phy error %d\n", retval); - goto err_u3d_enable; + clk_disable(u3d->clk); + goto err_phy_init; } } @@ -1974,15 +1979,13 @@ err_alloc_trb_pool: dma_free_coherent(&dev->dev, u3d->ep_context_size, u3d->ep_context, u3d->ep_context_dma); err_alloc_ep_context: - if (pdata->phy_deinit) - pdata->phy_deinit(u3d->phy_regs); - clk_disable(u3d->clk); +err_phy_init: err_u3d_enable: iounmap(u3d->cap_regs); err_map_cap_regs: err_get_cap_regs: -err_get_clk: clk_put(u3d->clk); +err_get_clk: kfree(u3d); err_alloc_private: err_pdata: diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 27ebb0d5449d..76f56c5762f9 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -445,7 +445,8 @@ static int mv_ep_enable(struct usb_ep *_ep, struct mv_dqh *dqh; u16 max = 0; u32 bit_pos, epctrlx, direction; - unsigned char zlt = 0, ios = 0, mult = 0; + const unsigned char zlt = 1; + unsigned char ios, mult; unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); @@ -465,8 +466,6 @@ static int mv_ep_enable(struct usb_ep *_ep, * disable HW zero length termination select * driver handles zero length packet through req->req.zero */ - zlt = 1; - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); /* Check if the Endpoint is Primed */ @@ -481,16 +480,16 @@ static int mv_ep_enable(struct usb_ep *_ep, (unsigned)bit_pos); goto en_done; } + /* Set the max packet length, interrupt on Setup and Mult fields */ + ios = 0; + mult = 0; switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: - zlt = 1; - mult = 0; + case USB_ENDPOINT_XFER_INT: break; case USB_ENDPOINT_XFER_CONTROL: ios = 1; - case USB_ENDPOINT_XFER_INT: - mult = 0; break; case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */ diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 7dc0102abdfe..8f85a51bd2b3 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -653,7 +653,7 @@ net2272_request_dma(struct net2272 *dev, unsigned ep, u32 buf, dev->dma_busy = 1; /* initialize platform's dma */ -#ifdef CONFIG_PCI +#ifdef CONFIG_USB_PCI /* NET2272 addr, buffer addr, length, etc. */ switch (dev->dev_id) { case PCI_DEVICE_ID_RDK1: @@ -701,7 +701,7 @@ static void net2272_start_dma(struct net2272 *dev) { /* start platform's dma controller */ -#ifdef CONFIG_PCI +#ifdef CONFIG_USB_PCI switch (dev->dev_id) { case PCI_DEVICE_ID_RDK1: writeb((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START), @@ -797,7 +797,7 @@ net2272_kick_dma(struct net2272_ep *ep, struct net2272_request *req) static void net2272_cancel_dma(struct net2272 *dev) { -#ifdef CONFIG_PCI +#ifdef CONFIG_USB_PCI switch (dev->dev_id) { case PCI_DEVICE_ID_RDK1: writeb(0, dev->rdk1.plx9054_base_addr + DMACSR0); @@ -2306,7 +2306,7 @@ err_add_udc: return ret; } -#ifdef CONFIG_PCI +#ifdef CONFIG_USB_PCI /* * wrap this driver around the specified device, but diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h index 127ab03fcde3..69bc9c3c6ce4 100644 --- a/drivers/usb/gadget/udc/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h @@ -472,7 +472,7 @@ struct net2272 { unsigned int base_shift; u16 __iomem *base_addr; union { -#ifdef CONFIG_PCI +#ifdef CONFIG_USB_PCI struct { void __iomem *plx9054_base_addr; void __iomem *epld_base_addr; diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 3828c2ec8623..6cf07857eaca 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -569,7 +569,7 @@ static struct usb_request if (ep->dma) { struct net2280_dma *td; - td = pci_pool_alloc(ep->dev->requests, gfp_flags, + td = dma_pool_alloc(ep->dev->requests, gfp_flags, &req->td_dma); if (!td) { kfree(req); @@ -597,7 +597,7 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) req = container_of(_req, struct net2280_request, req); WARN_ON(!list_empty(&req->queue)); if (req->td) - pci_pool_free(ep->dev->requests, req->td, req->td_dma); + dma_pool_free(ep->dev->requests, req->td, req->td_dma); kfree(req); } @@ -3579,10 +3579,10 @@ static void net2280_remove(struct pci_dev *pdev) for (i = 1; i < 5; i++) { if (!dev->ep[i].dummy) continue; - pci_pool_free(dev->requests, dev->ep[i].dummy, + dma_pool_free(dev->requests, dev->ep[i].dummy, dev->ep[i].td_dma); } - pci_pool_destroy(dev->requests); + dma_pool_destroy(dev->requests); } if (dev->got_irq) free_irq(pdev->irq, dev); @@ -3724,7 +3724,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* DMA setup */ /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ - dev->requests = pci_pool_create("requests", pdev, + dev->requests = dma_pool_create("requests", &pdev->dev, sizeof(struct net2280_dma), 0 /* no alignment requirements */, 0 /* or page-crossing issues */); @@ -3736,7 +3736,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) for (i = 1; i < 5; i++) { struct net2280_dma *td; - td = pci_pool_alloc(dev->requests, GFP_KERNEL, + td = dma_pool_alloc(dev->requests, GFP_KERNEL, &dev->ep[i].td_dma); if (!td) { ep_dbg(dev, "can't get dummy %d\n", i); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index 2736a95751c3..1088c3745999 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -187,7 +187,7 @@ struct net2280 { struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; struct usb338x_pl_regs __iomem *plregs; - struct pci_pool *requests; + struct dma_pool *requests; /* statistics...*/ }; diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 8a365aad66fe..84dcbcd756f0 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -355,8 +355,8 @@ struct pch_udc_dev { vbus_session:1, set_cfg_not_acked:1, waiting_zlp_ack:1; - struct pci_pool *data_requests; - struct pci_pool *stp_requests; + struct dma_pool *data_requests; + struct dma_pool *stp_requests; dma_addr_t dma_addr; struct usb_ctrlrequest setup_data; void __iomem *base_addr; @@ -1522,7 +1522,8 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev, /* do not free first desc., will be done by free for request */ td = phys_to_virt(addr); addr2 = (dma_addr_t)td->next; - pci_pool_free(dev->data_requests, td, addr); + dma_pool_free(dev->data_requests, td, addr); + td->next = 0x00; addr = addr2; } req->chain_len = 1; @@ -1538,7 +1539,7 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev, * * Return codes: * 0: success, - * -ENOMEM: pci_pool_alloc invocation fails + * -ENOMEM: dma_pool_alloc invocation fails */ static int pch_udc_create_dma_chain(struct pch_udc_ep *ep, struct pch_udc_request *req, @@ -1564,7 +1565,7 @@ static int pch_udc_create_dma_chain(struct pch_udc_ep *ep, if (bytes <= buf_len) break; last = td; - td = pci_pool_alloc(ep->dev->data_requests, gfp_flags, + td = dma_pool_alloc(ep->dev->data_requests, gfp_flags, &dma_addr); if (!td) goto nomem; @@ -1769,7 +1770,7 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep, if (!ep->dev->dma_addr) return &req->req; /* ep0 in requests are allocated from data pool here */ - dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp, + dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp, &req->td_data_phys); if (NULL == dma_desc) { kfree(req); @@ -1808,7 +1809,7 @@ static void pch_udc_free_request(struct usb_ep *usbep, if (req->td_data != NULL) { if (req->chain_len > 1) pch_udc_free_dma_chain(ep->dev, req); - pci_pool_free(ep->dev->data_requests, req->td_data, + dma_pool_free(ep->dev->data_requests, req->td_data, req->td_data_phys); } kfree(req); @@ -2913,7 +2914,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) void *ep0out_buf; /* DMA setup */ - dev->data_requests = pci_pool_create("data_requests", dev->pdev, + dev->data_requests = dma_pool_create("data_requests", &dev->pdev->dev, sizeof(struct pch_udc_data_dma_desc), 0, 0); if (!dev->data_requests) { dev_err(&dev->pdev->dev, "%s: can't get request data pool\n", @@ -2922,7 +2923,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) } /* dma desc for setup data */ - dev->stp_requests = pci_pool_create("setup requests", dev->pdev, + dev->stp_requests = dma_pool_create("setup requests", &dev->pdev->dev, sizeof(struct pch_udc_stp_dma_desc), 0, 0); if (!dev->stp_requests) { dev_err(&dev->pdev->dev, "%s: can't get setup request pool\n", @@ -2930,7 +2931,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) return -ENOMEM; } /* setup */ - td_stp = pci_pool_alloc(dev->stp_requests, GFP_KERNEL, + td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL, &dev->ep[UDC_EP0OUT_IDX].td_stp_phys); if (!td_stp) { dev_err(&dev->pdev->dev, @@ -2940,7 +2941,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) dev->ep[UDC_EP0OUT_IDX].td_stp = td_stp; /* data: 0 packets !? */ - td_data = pci_pool_alloc(dev->data_requests, GFP_KERNEL, + td_data = dma_pool_alloc(dev->data_requests, GFP_KERNEL, &dev->ep[UDC_EP0OUT_IDX].td_data_phys); if (!td_data) { dev_err(&dev->pdev->dev, @@ -3020,22 +3021,21 @@ static void pch_udc_remove(struct pci_dev *pdev) dev_err(&pdev->dev, "%s: gadget driver still bound!!!\n", __func__); /* dma pool cleanup */ - if (dev->data_requests) - pci_pool_destroy(dev->data_requests); + dma_pool_destroy(dev->data_requests); if (dev->stp_requests) { /* cleanup DMA desc's for ep0in */ if (dev->ep[UDC_EP0OUT_IDX].td_stp) { - pci_pool_free(dev->stp_requests, + dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IDX].td_stp, dev->ep[UDC_EP0OUT_IDX].td_stp_phys); } if (dev->ep[UDC_EP0OUT_IDX].td_data) { - pci_pool_free(dev->stp_requests, + dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IDX].td_data, dev->ep[UDC_EP0OUT_IDX].td_data_phys); } - pci_pool_destroy(dev->stp_requests); + dma_pool_destroy(dev->stp_requests); } if (dev->dma_addr) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 832c4fdbe985..d48e239660c3 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1608,9 +1608,6 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) return 0; } -static void udc_enable(struct pxa_udc *udc); -static void udc_disable(struct pxa_udc *udc); - /** * pxa_udc_vbus_session - Called by external transceiver to enable/disable udc * @_gadget: usb gadget diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 2218f91e92a6..5a2d845fb1a6 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/err.h> +#include <linux/extcon.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -37,6 +38,9 @@ #define USB3_USB_INT_ENA_2 0x22c #define USB3_STUP_DAT_0 0x230 #define USB3_STUP_DAT_1 0x234 +#define USB3_USB_OTG_STA 0x268 +#define USB3_USB_OTG_INT_STA 0x26c +#define USB3_USB_OTG_INT_ENA 0x270 #define USB3_P0_MOD 0x280 #define USB3_P0_CON 0x288 #define USB3_P0_STA 0x28c @@ -124,6 +128,9 @@ /* USB_INT_ENA_2 and USB_INT_STA_2 */ #define USB_INT_2_PIPE(n) BIT(n) +/* USB_OTG_STA, USB_OTG_INT_STA and USB_OTG_INT_ENA */ +#define USB_OTG_IDMON BIT(4) + /* P0_MOD */ #define P0_MOD_DIR BIT(6) @@ -257,6 +264,8 @@ struct renesas_usb3 { struct usb_gadget gadget; struct usb_gadget_driver *driver; + struct extcon_dev *extcon; + struct work_struct extcon_work; struct renesas_usb3_ep *usb3_ep; int num_usb3_eps; @@ -269,6 +278,8 @@ struct renesas_usb3 { u8 ep0_buf[USB3_EP0_BUF_SIZE]; bool softconnect; bool workaround_for_vbus; + bool extcon_host; /* check id and set EXTCON_USB_HOST */ + bool extcon_usb; /* check vbus and set EXTCON_USB */ }; #define gadget_to_renesas_usb3(_gadget) \ @@ -332,6 +343,15 @@ static int usb3_wait(struct renesas_usb3 *usb3, u32 reg, u32 mask, return -EBUSY; } +static void renesas_usb3_extcon_work(struct work_struct *work) +{ + struct renesas_usb3 *usb3 = container_of(work, struct renesas_usb3, + extcon_work); + + extcon_set_state_sync(usb3->extcon, EXTCON_USB_HOST, usb3->extcon_host); + extcon_set_state_sync(usb3->extcon, EXTCON_USB, usb3->extcon_usb); +} + static void usb3_enable_irq_1(struct renesas_usb3 *usb3, u32 bits) { usb3_set_bit(usb3, bits, USB3_USB_INT_ENA_1); @@ -352,6 +372,11 @@ static void usb3_disable_pipe_irq(struct renesas_usb3 *usb3, int num) usb3_clear_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2); } +static bool usb3_is_host(struct renesas_usb3 *usb3) +{ + return !(usb3_read(usb3, USB3_DRD_CON) & DRD_CON_PERI_CON); +} + static void usb3_init_axi_bridge(struct renesas_usb3 *usb3) { /* Set AXI_INT */ @@ -362,10 +387,6 @@ static void usb3_init_axi_bridge(struct renesas_usb3 *usb3) static void usb3_init_epc_registers(struct renesas_usb3 *usb3) { - /* FIXME: How to change host / peripheral mode as well? */ - usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON); - usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON); - usb3_write(usb3, ~0, USB3_USB_INT_STA_1); usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG); } @@ -531,18 +552,70 @@ static void usb3_check_vbus(struct renesas_usb3 *usb3) if (usb3->workaround_for_vbus) { usb3_connect(usb3); } else { - if (usb3_read(usb3, USB3_USB_STA) & USB_STA_VBUS_STA) + usb3->extcon_usb = !!(usb3_read(usb3, USB3_USB_STA) & + USB_STA_VBUS_STA); + if (usb3->extcon_usb) usb3_connect(usb3); else usb3_disconnect(usb3); + + schedule_work(&usb3->extcon_work); } } +static void usb3_set_mode(struct renesas_usb3 *usb3, bool host) +{ + if (host) + usb3_clear_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON); + else + usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON); +} + +static void usb3_vbus_out(struct renesas_usb3 *usb3, bool enable) +{ + if (enable) + usb3_set_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON); + else + usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON); +} + +static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&usb3->lock, flags); + usb3_set_mode(usb3, host); + usb3_vbus_out(usb3, a_dev); + if (!host && a_dev) /* for A-Peripheral */ + usb3_connect(usb3); + spin_unlock_irqrestore(&usb3->lock, flags); +} + +static bool usb3_is_a_device(struct renesas_usb3 *usb3) +{ + return !(usb3_read(usb3, USB3_USB_OTG_STA) & USB_OTG_IDMON); +} + +static void usb3_check_id(struct renesas_usb3 *usb3) +{ + usb3->extcon_host = usb3_is_a_device(usb3); + + if (usb3->extcon_host) + usb3_mode_config(usb3, true, true); + else + usb3_mode_config(usb3, false, false); + + schedule_work(&usb3->extcon_work); +} + static void renesas_usb3_init_controller(struct renesas_usb3 *usb3) { usb3_init_axi_bridge(usb3); usb3_init_epc_registers(usb3); + usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_STA); + usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_ENA); + usb3_check_id(usb3); usb3_check_vbus(usb3); } @@ -551,6 +624,7 @@ static void renesas_usb3_stop_controller(struct renesas_usb3 *usb3) usb3_disconnect(usb3); usb3_write(usb3, 0, USB3_P0_INT_ENA); usb3_write(usb3, 0, USB3_PN_INT_ENA); + usb3_write(usb3, 0, USB3_USB_OTG_INT_ENA); usb3_write(usb3, 0, USB3_USB_INT_ENA_1); usb3_write(usb3, 0, USB3_USB_INT_ENA_2); usb3_write(usb3, 0, USB3_AXI_INT_ENA); @@ -1474,10 +1548,22 @@ static void usb3_irq_epc_int_2(struct renesas_usb3 *usb3, u32 int_sta_2) } } +static void usb3_irq_idmon_change(struct renesas_usb3 *usb3) +{ + usb3_check_id(usb3); +} + +static void usb3_irq_otg_int(struct renesas_usb3 *usb3, u32 otg_int_sta) +{ + if (otg_int_sta & USB_OTG_IDMON) + usb3_irq_idmon_change(usb3); +} + static void usb3_irq_epc(struct renesas_usb3 *usb3) { u32 int_sta_1 = usb3_read(usb3, USB3_USB_INT_STA_1); u32 int_sta_2 = usb3_read(usb3, USB3_USB_INT_STA_2); + u32 otg_int_sta = usb3_read(usb3, USB3_USB_OTG_INT_STA); int_sta_1 &= usb3_read(usb3, USB3_USB_INT_ENA_1); if (int_sta_1) { @@ -1488,6 +1574,12 @@ static void usb3_irq_epc(struct renesas_usb3 *usb3) int_sta_2 &= usb3_read(usb3, USB3_USB_INT_ENA_2); if (int_sta_2) usb3_irq_epc_int_2(usb3, int_sta_2); + + otg_int_sta &= usb3_read(usb3, USB3_USB_OTG_INT_ENA); + if (otg_int_sta) { + usb3_write(usb3, otg_int_sta, USB3_USB_OTG_INT_STA); + usb3_irq_otg_int(usb3, otg_int_sta); + } } static irqreturn_t renesas_usb3_irq(int irq, void *_usb3) @@ -1756,11 +1848,49 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = { .set_selfpowered = renesas_usb3_set_selfpowered, }; +static ssize_t role_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + bool new_mode_is_host; + + if (!usb3->driver) + return -ENODEV; + + if (!strncmp(buf, "host", strlen("host"))) + new_mode_is_host = true; + else if (!strncmp(buf, "peripheral", strlen("peripheral"))) + new_mode_is_host = false; + else + return -EINVAL; + + if (new_mode_is_host == usb3_is_host(usb3)) + return -EINVAL; + + usb3_mode_config(usb3, new_mode_is_host, usb3_is_a_device(usb3)); + + return count; +} + +static ssize_t role_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + + if (!usb3->driver) + return -ENODEV; + + return sprintf(buf, "%s\n", usb3_is_host(usb3) ? "host" : "peripheral"); +} +static DEVICE_ATTR_RW(role); + /*------- platform_driver ------------------------------------------------*/ static int renesas_usb3_remove(struct platform_device *pdev) { struct renesas_usb3 *usb3 = platform_get_drvdata(pdev); + device_remove_file(&pdev->dev, &dev_attr_role); + pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1894,6 +2024,12 @@ static const struct of_device_id usb3_of_match[] = { }; MODULE_DEVICE_TABLE(of, usb3_of_match); +static const unsigned int renesas_usb3_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; @@ -1937,6 +2073,17 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (ret < 0) return ret; + INIT_WORK(&usb3->extcon_work, renesas_usb3_extcon_work); + usb3->extcon = devm_extcon_dev_allocate(&pdev->dev, renesas_usb3_cable); + if (IS_ERR(usb3->extcon)) + return PTR_ERR(usb3->extcon); + + ret = devm_extcon_dev_register(&pdev->dev, usb3->extcon); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register extcon\n"); + return ret; + } + /* for ep0 handling */ usb3->ep0_req = __renesas_usb3_ep_alloc_request(GFP_KERNEL); if (!usb3->ep0_req) @@ -1946,6 +2093,10 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (ret < 0) goto err_add_udc; + ret = device_create_file(&pdev->dev, &dev_attr_role); + if (ret < 0) + goto err_dev_create; + usb3->workaround_for_vbus = priv->workaround_for_vbus; pm_runtime_enable(&pdev->dev); @@ -1955,6 +2106,9 @@ static int renesas_usb3_probe(struct platform_device *pdev) return 0; +err_dev_create: + usb_del_gadget_udc(&usb3->gadget); + err_add_udc: __renesas_usb3_ep_free_request(usb3->ep0_req); |