diff options
Diffstat (limited to 'drivers/usb/gadget/udc')
-rw-r--r-- | drivers/usb/gadget/udc/Kconfig | 18 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/Makefile | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/amd5536udc.h | 18 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/amd5536udc_pci.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.h | 27 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/bdc/bdc_core.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/core.c | 46 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/dummy_hcd.c | 39 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/mv_udc_core.c | 9 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/net2280.c | 5 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/renesas_usb3.c | 392 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/snps_udc_core.c (renamed from drivers/usb/gadget/udc/amd5536udc.c) | 80 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/snps_udc_plat.c | 344 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/udc-xilinx.c | 2 |
15 files changed, 905 insertions, 85 deletions
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 1c14c283cc47..9ffb11ec9ed9 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -55,7 +55,7 @@ config USB_LPC32XX config USB_ATMEL_USBA tristate "Atmel USBA" - depends on ((AVR32 && !OF) || ARCH_AT91) + depends on ARCH_AT91 help USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. @@ -256,7 +256,7 @@ config USB_MV_U3D controller, which support super speed USB peripheral. config USB_SNP_CORE - depends on USB_AMD5536UDC + depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT) tristate help This enables core driver support for Synopsys USB 2.0 Device @@ -269,6 +269,20 @@ config USB_SNP_CORE This IP is different to the High Speed OTG IP that can be enabled by selecting USB_DWC2 or USB_DWC3 options. +config USB_SNP_UDC_PLAT + tristate "Synopsys USB 2.0 Device controller" + depends on (USB_GADGET && OF) + select USB_GADGET_DUALSPEED + select USB_SNP_CORE + default ARCH_BCM_IPROC + help + This adds Platform Device support for Synopsys Designware core + AHB subsystem USB2.0 Device Controller (UDC). + + This driver works with UDCs integrated into Broadcom's Northstar2 + and Cygnus SoCs. + + If unsure, say N. # # Controllers available in both integrated and discrete versions # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 626e1f1c62da..ea9e1c7f1923 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -10,7 +10,7 @@ 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_SNP_CORE) += amd5536udc.o +obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o @@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o obj-$(CONFIG_USB_GR_UDC) += gr_udc.o obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o +obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o obj-$(CONFIG_USB_BDC_UDC) += bdc/ diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index fae49bf3833e..4fe22d432af2 100644 --- a/drivers/usb/gadget/udc/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h @@ -16,6 +16,7 @@ /* debug control */ /* #define UDC_VERBOSE */ +#include <linux/extcon.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -28,6 +29,9 @@ #define UDC_HSA0_REV 1 #define UDC_HSB1_REV 2 +/* Broadcom chip rev. */ +#define UDC_BCM_REV 10 + /* * SETUP usb commands * needed, because some SETUP's are handled in hw, but must be passed to @@ -112,6 +116,7 @@ #define UDC_DEVCTL_BRLEN_MASK 0x00ff0000 #define UDC_DEVCTL_BRLEN_OFS 16 +#define UDC_DEVCTL_SRX_FLUSH 14 #define UDC_DEVCTL_CSR_DONE 13 #define UDC_DEVCTL_DEVNAK 12 #define UDC_DEVCTL_SD 10 @@ -563,6 +568,16 @@ struct udc { u16 cur_config; u16 cur_intf; u16 cur_alt; + + /* for platform device and extcon support */ + struct device *dev; + struct phy *udc_phy; + struct extcon_dev *edev; + struct extcon_specific_cable_nb extcon_nb; + struct notifier_block nb; + struct delayed_work drd_work; + struct workqueue_struct *drd_wq; + u32 conn_type; }; #define to_amd5536_udc(g) (container_of((g), struct udc, gadget)) @@ -578,6 +593,7 @@ 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 empty_req_queue(struct udc_ep *ep); void udc_basic_init(struct udc *dev); void free_dma_pools(struct udc *dev); int init_dma_pools(struct udc *dev); @@ -639,7 +655,7 @@ MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only"); /* debug macros ------------------------------------------------------------*/ -#define DBG(udc , args...) dev_dbg(&(udc)->pdev->dev, args) +#define DBG(udc , args...) dev_dbg(udc->dev, args) #ifdef UDC_VERBOSE #define VDBG DBG diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index 2a2d0a96fe24..57a13f080a79 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -168,6 +168,7 @@ static int udc_pci_probe( dev->phys_addr = resource; dev->irq = pdev->irq; dev->pdev = pdev; + dev->dev = &pdev->dev; /* general probing */ if (udc_probe(dev)) { diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 3ccc34176a5a..98d71400f8a1 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file) spin_lock_irq(&udc->lock); for (i = 0; i < inode->i_size / 4; i++) - data[i] = usba_io_readl(udc->regs + i * 4); + data[i] = readl_relaxed(udc->regs + i * 4); spin_unlock_irq(&udc->lock); file->private_data = data; @@ -1369,7 +1369,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, if (crq->wLength != cpu_to_le16(sizeof(status))) goto stall; ep->state = DATA_STAGE_IN; - usba_io_writew(status, ep->fifo); + writew_relaxed(status, ep->fifo); usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); break; } diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 9551b704bfd3..f8ebe0389bd4 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -43,13 +43,8 @@ #define USBA_REMOTE_WAKE_UP (1 << 10) #define USBA_PULLD_DIS (1 << 11) -#if defined(CONFIG_AVR32) -#define USBA_ENABLE_MASK USBA_EN_USBA -#define USBA_DISABLE_MASK 0 -#elif defined(CONFIG_ARCH_AT91) #define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS) #define USBA_DISABLE_MASK USBA_DETACH -#endif /* CONFIG_ARCH_AT91 */ /* Bitfields in FNUM */ #define USBA_MICRO_FRAME_NUM_OFFSET 0 @@ -191,28 +186,18 @@ | USBA_BF(name, value)) /* Register access macros */ -#ifdef CONFIG_AVR32 -#define usba_io_readl __raw_readl -#define usba_io_writel __raw_writel -#define usba_io_writew __raw_writew -#else -#define usba_io_readl readl_relaxed -#define usba_io_writel writel_relaxed -#define usba_io_writew writew_relaxed -#endif - #define usba_readl(udc, reg) \ - usba_io_readl((udc)->regs + USBA_##reg) + readl_relaxed((udc)->regs + USBA_##reg) #define usba_writel(udc, reg, value) \ - usba_io_writel((value), (udc)->regs + USBA_##reg) + writel_relaxed((value), (udc)->regs + USBA_##reg) #define usba_ep_readl(ep, reg) \ - usba_io_readl((ep)->ep_regs + USBA_EPT_##reg) + readl_relaxed((ep)->ep_regs + USBA_EPT_##reg) #define usba_ep_writel(ep, reg, value) \ - usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg) + writel_relaxed((value), (ep)->ep_regs + USBA_EPT_##reg) #define usba_dma_readl(ep, reg) \ - usba_io_readl((ep)->dma_regs + USBA_DMA_##reg) + readl_relaxed((ep)->dma_regs + USBA_DMA_##reg) #define usba_dma_writel(ep, reg, value) \ - usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + writel_relaxed((value), (ep)->dma_regs + USBA_DMA_##reg) /* Calculate base address for a given endpoint or DMA controller */ #define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index ccb9c213cc9f..e9bd8d4abca0 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -475,7 +475,7 @@ static int bdc_probe(struct platform_device *pdev) bdc->dev = dev; dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq); - temp = bdc_readl(bdc->regs, BDC_BDCSC); + temp = bdc_readl(bdc->regs, BDC_BDCCAP1); if ((temp & BDC_P64) && !dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) { dev_dbg(bdc->dev, "Using 64-bit address\n"); diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index efce68e9a8e0..e6f04eee95c4 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -23,6 +23,7 @@ #include <linux/list.h> #include <linux/err.h> #include <linux/dma-mapping.h> +#include <linux/sched/task_stack.h> #include <linux/workqueue.h> #include <linux/usb/ch9.h> @@ -139,10 +140,8 @@ int usb_ep_disable(struct usb_ep *ep) goto out; ret = ep->ops->disable(ep); - if (ret) { - ret = ret; + if (ret) goto out; - } ep->enabled = false; @@ -798,6 +797,14 @@ int usb_gadget_map_request_by_dev(struct device *dev, req->num_mapped_sgs = mapped; } else { + if (is_vmalloc_addr(req->buf)) { + dev_err(dev, "buffer is not dma capable\n"); + return -EFAULT; + } else if (object_is_on_stack(req->buf)) { + dev_err(dev, "buffer is on stack\n"); + return -EFAULT; + } + req->dma = dma_map_single(dev, req->buf, req->length, is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); @@ -1058,6 +1065,23 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc) } /** + * usb_gadget_udc_set_speed - tells usb device controller speed supported by + * current driver + * @udc: The device we want to set maximum speed + * @speed: The maximum speed to allowed to run + * + * This call is issued by the UDC Class driver before calling + * usb_gadget_udc_start() in order to make sure that we don't try to + * connect on speeds the gadget driver doesn't support. + */ +static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, + enum usb_device_speed speed) +{ + if (udc->gadget->ops->udc_set_speed) + udc->gadget->ops->udc_set_speed(udc->gadget, speed); +} + +/** * usb_udc_release - release the usb_udc struct * @dev: the dev member within usb_udc * @@ -1290,6 +1314,9 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri udc->dev.driver = &driver->driver; udc->gadget->dev.driver = &driver->driver; + if (driver->max_speed < udc->gadget->max_speed) + usb_gadget_udc_set_speed(udc, driver->max_speed); + ret = driver->bind(udc->gadget, driver); if (ret) goto err1; @@ -1442,6 +1469,18 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(state); +static ssize_t function_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + struct usb_gadget_driver *drv = udc->driver; + + if (!drv || !drv->function) + return 0; + return scnprintf(buf, PAGE_SIZE, "%s\n", drv->function); +} +static DEVICE_ATTR_RO(function); + #define USB_UDC_SPEED_ATTR(name, param) \ ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ @@ -1477,6 +1516,7 @@ static struct attribute *usb_udc_attrs[] = { &dev_attr_srp.attr, &dev_attr_soft_connect.attr, &dev_attr_state.attr, + &dev_attr_function.attr, &dev_attr_current_speed.attr, &dev_attr_maximum_speed.attr, diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 7635fd7cc328..3c3760315910 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -881,22 +881,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value) unsigned long flags; dum = gadget_dev_to_dummy(&_gadget->dev); - - if (value && dum->driver) { - if (mod_data.is_super_speed) - dum->gadget.speed = dum->driver->max_speed; - else if (mod_data.is_high_speed) - dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, - dum->driver->max_speed); - else - dum->gadget.speed = USB_SPEED_FULL; - dummy_udc_update_ep0(dum); - - if (dum->gadget.speed < dum->driver->max_speed) - dev_dbg(udc_dev(dum), "This device can perform faster" - " if you connect it to a %s port...\n", - usb_speed_string(dum->driver->max_speed)); - } dum_hcd = gadget_to_dummy_hcd(_gadget); spin_lock_irqsave(&dum->lock, flags); @@ -908,6 +892,28 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value) return 0; } +static void dummy_udc_set_speed(struct usb_gadget *_gadget, + enum usb_device_speed speed) +{ + struct dummy *dum; + + dum = gadget_dev_to_dummy(&_gadget->dev); + + if (mod_data.is_super_speed) + dum->gadget.speed = min_t(u8, USB_SPEED_SUPER, speed); + else if (mod_data.is_high_speed) + dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed); + else + dum->gadget.speed = USB_SPEED_FULL; + + dummy_udc_update_ep0(dum); + + if (dum->gadget.speed < speed) + dev_dbg(udc_dev(dum), "This device can perform faster" + " if you connect it to a %s port...\n", + usb_speed_string(speed)); +} + static int dummy_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver); static int dummy_udc_stop(struct usb_gadget *g); @@ -919,6 +925,7 @@ static const struct usb_gadget_ops dummy_ops = { .pullup = dummy_pullup, .udc_start = dummy_udc_start, .udc_stop = dummy_udc_stop, + .udc_set_speed = dummy_udc_set_speed, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 76f56c5762f9..8a708d0a1042 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -960,9 +960,9 @@ static const struct usb_ep_ops mv_ep_ops = { .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ }; -static void udc_clock_enable(struct mv_udc *udc) +static int udc_clock_enable(struct mv_udc *udc) { - clk_prepare_enable(udc->clk); + return clk_prepare_enable(udc->clk); } static void udc_clock_disable(struct mv_udc *udc) @@ -1070,7 +1070,10 @@ static int mv_udc_enable_internal(struct mv_udc *udc) return 0; dev_dbg(&udc->dev->dev, "enable udc\n"); - udc_clock_enable(udc); + retval = udc_clock_enable(udc); + if (retval) + return retval; + if (udc->pdata->phy_init) { retval = udc->pdata->phy_init(udc->phy_regs); if (retval) { diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index f2cbd7f8005e..f608c1f85e61 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3566,7 +3566,6 @@ static void net2280_remove(struct pci_dev *pdev) BUG_ON(dev->driver); /* then clean up the resources we allocated during probe() */ - net2280_led_shutdown(dev); if (dev->requests) { int i; for (i = 1; i < 5; i++) { @@ -3581,8 +3580,10 @@ static void net2280_remove(struct pci_dev *pdev) free_irq(pdev->irq, dev); if (dev->quirks & PLX_PCIE) pci_disable_msi(pdev); - if (dev->regs) + if (dev->regs) { + net2280_led_shutdown(dev); iounmap(dev->regs); + } if (dev->region) release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index cd4c88529721..d8278322d5ac 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -9,6 +9,7 @@ */ #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/extcon.h> #include <linux/interrupt.h> @@ -27,6 +28,8 @@ #define USB3_AXI_INT_ENA 0x00c #define USB3_DMA_INT_STA 0x010 #define USB3_DMA_INT_ENA 0x014 +#define USB3_DMA_CH0_CON(n) (0x030 + ((n) - 1) * 0x10) /* n = 1 to 4 */ +#define USB3_DMA_CH0_PRD_ADR(n) (0x034 + ((n) - 1) * 0x10) /* n = 1 to 4 */ #define USB3_USB_COM_CON 0x200 #define USB3_USB20_CON 0x204 #define USB3_USB30_CON 0x208 @@ -64,6 +67,22 @@ /* AXI_INT_ENA and AXI_INT_STA */ #define AXI_INT_DMAINT BIT(31) #define AXI_INT_EPCINT BIT(30) +/* PRD's n = from 1 to 4 */ +#define AXI_INT_PRDEN_CLR_STA_SHIFT(n) (16 + (n) - 1) +#define AXI_INT_PRDERR_STA_SHIFT(n) (0 + (n) - 1) +#define AXI_INT_PRDEN_CLR_STA(n) (1 << AXI_INT_PRDEN_CLR_STA_SHIFT(n)) +#define AXI_INT_PRDERR_STA(n) (1 << AXI_INT_PRDERR_STA_SHIFT(n)) + +/* DMA_INT_ENA and DMA_INT_STA */ +#define DMA_INT(n) BIT(n) + +/* DMA_CH0_CONn */ +#define DMA_CON_PIPE_DIR BIT(15) /* 1: In Transfer */ +#define DMA_CON_PIPE_NO_SHIFT 8 +#define DMA_CON_PIPE_NO_MASK GENMASK(12, DMA_CON_PIPE_NO_SHIFT) +#define DMA_COM_PIPE_NO(n) (((n) << DMA_CON_PIPE_NO_SHIFT) & \ + DMA_CON_PIPE_NO_MASK) +#define DMA_CON_PRD_EN BIT(0) /* LCLKSEL */ #define LCLKSEL_LSEL BIT(18) @@ -231,8 +250,50 @@ #define USB3_EP0_BUF_SIZE 8 #define USB3_MAX_NUM_PIPES 30 #define USB3_WAIT_US 3 +#define USB3_DMA_NUM_SETTING_AREA 4 +/* + * To avoid double-meaning of "0" (xferred 65536 bytes or received zlp if + * buffer size is 65536), this driver uses the maximum size per a entry is + * 32768 bytes. + */ +#define USB3_DMA_MAX_XFER_SIZE 32768 +#define USB3_DMA_PRD_SIZE 4096 struct renesas_usb3; + +/* Physical Region Descriptor Table */ +struct renesas_usb3_prd { + u32 word1; +#define USB3_PRD1_E BIT(30) /* the end of chain */ +#define USB3_PRD1_U BIT(29) /* completion of transfer */ +#define USB3_PRD1_D BIT(28) /* Error occurred */ +#define USB3_PRD1_INT BIT(27) /* Interrupt occurred */ +#define USB3_PRD1_LST BIT(26) /* Last Packet */ +#define USB3_PRD1_B_INC BIT(24) +#define USB3_PRD1_MPS_8 0 +#define USB3_PRD1_MPS_16 BIT(21) +#define USB3_PRD1_MPS_32 BIT(22) +#define USB3_PRD1_MPS_64 (BIT(22) | BIT(21)) +#define USB3_PRD1_MPS_512 BIT(23) +#define USB3_PRD1_MPS_1024 (BIT(23) | BIT(21)) +#define USB3_PRD1_MPS_RESERVED (BIT(23) | BIT(22) | BIT(21)) +#define USB3_PRD1_SIZE_MASK GENMASK(15, 0) + + u32 bap; +}; +#define USB3_DMA_NUM_PRD_ENTRIES (USB3_DMA_PRD_SIZE / \ + sizeof(struct renesas_usb3_prd)) +#define USB3_DMA_MAX_XFER_SIZE_ALL_PRDS (USB3_DMA_PRD_SIZE / \ + sizeof(struct renesas_usb3_prd) * \ + USB3_DMA_MAX_XFER_SIZE) + +struct renesas_usb3_dma { + struct renesas_usb3_prd *prd; + dma_addr_t prd_dma; + int num; /* Setting area number (from 1 to 4) */ + bool used; +}; + struct renesas_usb3_request { struct usb_request req; struct list_head queue; @@ -242,6 +303,7 @@ struct renesas_usb3_request { struct renesas_usb3_ep { struct usb_ep ep; struct renesas_usb3 *usb3; + struct renesas_usb3_dma *dma; int num; char ep_name[USB3_EP_NAME_SIZE]; struct list_head queue; @@ -270,6 +332,8 @@ struct renesas_usb3 { struct renesas_usb3_ep *usb3_ep; int num_usb3_eps; + struct renesas_usb3_dma dma[USB3_DMA_NUM_SETTING_AREA]; + spinlock_t lock; int disabled_count; @@ -298,8 +362,18 @@ struct renesas_usb3 { (i) < (usb3)->num_usb3_eps; \ (i)++, usb3_ep = usb3_get_ep(usb3, (i))) +#define usb3_get_dma(usb3, i) (&(usb3)->dma[i]) +#define usb3_for_each_dma(usb3, dma, i) \ + for ((i) = 0, dma = usb3_get_dma((usb3), (i)); \ + (i) < USB3_DMA_NUM_SETTING_AREA; \ + (i)++, dma = usb3_get_dma((usb3), (i))) + static const char udc_name[] = "renesas_usb3"; +static bool use_dma = 1; +module_param(use_dma, bool, 0644); +MODULE_PARM_DESC(use_dma, "use dedicated DMAC"); + static void usb3_write(struct renesas_usb3 *usb3, u32 data, u32 offs) { iowrite32(data, usb3->reg + offs); @@ -1059,6 +1133,273 @@ static void usb3_start_pipe0(struct renesas_usb3_ep *usb3_ep, usb3_p0_xfer(usb3_ep, usb3_req); } +static void usb3_enable_dma_pipen(struct renesas_usb3 *usb3) +{ + usb3_set_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON); +} + +static void usb3_disable_dma_pipen(struct renesas_usb3 *usb3) +{ + usb3_clear_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON); +} + +static void usb3_enable_dma_irq(struct renesas_usb3 *usb3, int num) +{ + usb3_set_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA); +} + +static void usb3_disable_dma_irq(struct renesas_usb3 *usb3, int num) +{ + usb3_clear_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA); +} + +static u32 usb3_dma_mps_to_prd_word1(struct renesas_usb3_ep *usb3_ep) +{ + switch (usb3_ep->ep.maxpacket) { + case 8: + return USB3_PRD1_MPS_8; + case 16: + return USB3_PRD1_MPS_16; + case 32: + return USB3_PRD1_MPS_32; + case 64: + return USB3_PRD1_MPS_64; + case 512: + return USB3_PRD1_MPS_512; + case 1024: + return USB3_PRD1_MPS_1024; + default: + return USB3_PRD1_MPS_RESERVED; + } +} + +static bool usb3_dma_get_setting_area(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + struct renesas_usb3_dma *dma; + int i; + bool ret = false; + + if (usb3_req->req.length > USB3_DMA_MAX_XFER_SIZE_ALL_PRDS) { + dev_dbg(usb3_to_dev(usb3), "%s: the length is too big (%d)\n", + __func__, usb3_req->req.length); + return false; + } + + /* The driver doesn't handle zero-length packet via dmac */ + if (!usb3_req->req.length) + return false; + + if (usb3_dma_mps_to_prd_word1(usb3_ep) == USB3_PRD1_MPS_RESERVED) + return false; + + usb3_for_each_dma(usb3, dma, i) { + if (dma->used) + continue; + + if (usb_gadget_map_request(&usb3->gadget, &usb3_req->req, + usb3_ep->dir_in) < 0) + break; + + dma->used = true; + usb3_ep->dma = dma; + ret = true; + break; + } + + return ret; +} + +static void usb3_dma_put_setting_area(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + int i; + struct renesas_usb3_dma *dma; + + usb3_for_each_dma(usb3, dma, i) { + if (usb3_ep->dma == dma) { + usb_gadget_unmap_request(&usb3->gadget, &usb3_req->req, + usb3_ep->dir_in); + dma->used = false; + usb3_ep->dma = NULL; + break; + } + } +} + +static void usb3_dma_fill_prd(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd; + u32 remain = usb3_req->req.length; + u32 dma = usb3_req->req.dma; + u32 len; + int i = 0; + + do { + len = min_t(u32, remain, USB3_DMA_MAX_XFER_SIZE) & + USB3_PRD1_SIZE_MASK; + cur_prd->word1 = usb3_dma_mps_to_prd_word1(usb3_ep) | + USB3_PRD1_B_INC | len; + cur_prd->bap = dma; + remain -= len; + dma += len; + if (!remain || (i + 1) < USB3_DMA_NUM_PRD_ENTRIES) + break; + + cur_prd++; + i++; + } while (1); + + cur_prd->word1 |= USB3_PRD1_E | USB3_PRD1_INT; + if (usb3_ep->dir_in) + cur_prd->word1 |= USB3_PRD1_LST; +} + +static void usb3_dma_kick_prd(struct renesas_usb3_ep *usb3_ep) +{ + struct renesas_usb3_dma *dma = usb3_ep->dma; + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + u32 dma_con = DMA_COM_PIPE_NO(usb3_ep->num) | DMA_CON_PRD_EN; + + if (usb3_ep->dir_in) + dma_con |= DMA_CON_PIPE_DIR; + + wmb(); /* prd entries should be in system memory here */ + + usb3_write(usb3, 1 << usb3_ep->num, USB3_DMA_INT_STA); + usb3_write(usb3, AXI_INT_PRDEN_CLR_STA(dma->num) | + AXI_INT_PRDERR_STA(dma->num), USB3_AXI_INT_STA); + + usb3_write(usb3, dma->prd_dma, USB3_DMA_CH0_PRD_ADR(dma->num)); + usb3_write(usb3, dma_con, USB3_DMA_CH0_CON(dma->num)); + usb3_enable_dma_irq(usb3, usb3_ep->num); +} + +static void usb3_dma_stop_prd(struct renesas_usb3_ep *usb3_ep) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + struct renesas_usb3_dma *dma = usb3_ep->dma; + + usb3_disable_dma_irq(usb3, usb3_ep->num); + usb3_write(usb3, 0, USB3_DMA_CH0_CON(dma->num)); +} + +static int usb3_dma_update_status(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd; + struct usb_request *req = &usb3_req->req; + u32 remain, len; + int i = 0; + int status = 0; + + rmb(); /* The controller updated prd entries */ + + do { + if (cur_prd->word1 & USB3_PRD1_D) + status = -EIO; + if (cur_prd->word1 & USB3_PRD1_E) + len = req->length % USB3_DMA_MAX_XFER_SIZE; + else + len = USB3_DMA_MAX_XFER_SIZE; + remain = cur_prd->word1 & USB3_PRD1_SIZE_MASK; + req->actual += len - remain; + + if (cur_prd->word1 & USB3_PRD1_E || + (i + 1) < USB3_DMA_NUM_PRD_ENTRIES) + break; + + cur_prd++; + i++; + } while (1); + + return status; +} + +static bool usb3_dma_try_start(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + + if (!use_dma) + return false; + + if (usb3_dma_get_setting_area(usb3_ep, usb3_req)) { + usb3_pn_stop(usb3); + usb3_enable_dma_pipen(usb3); + usb3_dma_fill_prd(usb3_ep, usb3_req); + usb3_dma_kick_prd(usb3_ep); + usb3_pn_start(usb3); + return true; + } + + return false; +} + +static int usb3_dma_try_stop(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + unsigned long flags; + int status = 0; + + spin_lock_irqsave(&usb3->lock, flags); + if (!usb3_ep->dma) + goto out; + + if (!usb3_pn_change(usb3, usb3_ep->num)) + usb3_disable_dma_pipen(usb3); + usb3_dma_stop_prd(usb3_ep); + status = usb3_dma_update_status(usb3_ep, usb3_req); + usb3_dma_put_setting_area(usb3_ep, usb3_req); + +out: + spin_unlock_irqrestore(&usb3->lock, flags); + return status; +} + +static int renesas_usb3_dma_free_prd(struct renesas_usb3 *usb3, + struct device *dev) +{ + int i; + struct renesas_usb3_dma *dma; + + usb3_for_each_dma(usb3, dma, i) { + if (dma->prd) { + dma_free_coherent(dev, USB3_DMA_MAX_XFER_SIZE, + dma->prd, dma->prd_dma); + dma->prd = NULL; + } + } + + return 0; +} + +static int renesas_usb3_dma_alloc_prd(struct renesas_usb3 *usb3, + struct device *dev) +{ + int i; + struct renesas_usb3_dma *dma; + + if (!use_dma) + return 0; + + usb3_for_each_dma(usb3, dma, i) { + dma->prd = dma_alloc_coherent(dev, USB3_DMA_PRD_SIZE, + &dma->prd_dma, GFP_KERNEL); + if (!dma->prd) { + renesas_usb3_dma_free_prd(usb3, dev); + return -ENOMEM; + } + dma->num = i + 1; + } + + return 0; +} + static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep, struct renesas_usb3_request *usb3_req) { @@ -1078,6 +1419,10 @@ static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep, goto out; usb3_ep->started = true; + + if (usb3_dma_try_start(usb3_ep, usb3_req)) + goto out; + usb3_pn_start(usb3); if (usb3_ep->dir_in) { @@ -1603,12 +1948,49 @@ static void usb3_irq_epc(struct renesas_usb3 *usb3) } } +static void usb3_irq_dma_int(struct renesas_usb3 *usb3, u32 dma_sta) +{ + struct renesas_usb3_ep *usb3_ep; + struct renesas_usb3_request *usb3_req; + int i, status; + + for (i = 0; i < usb3->num_usb3_eps; i++) { + if (!(dma_sta & DMA_INT(i))) + continue; + + usb3_ep = usb3_get_ep(usb3, i); + if (!(usb3_read(usb3, USB3_AXI_INT_STA) & + AXI_INT_PRDEN_CLR_STA(usb3_ep->dma->num))) + continue; + + usb3_req = usb3_get_request(usb3_ep); + status = usb3_dma_try_stop(usb3_ep, usb3_req); + usb3_request_done_pipen(usb3, usb3_ep, usb3_req, status); + } +} + +static void usb3_irq_dma(struct renesas_usb3 *usb3) +{ + u32 dma_sta = usb3_read(usb3, USB3_DMA_INT_STA); + + dma_sta &= usb3_read(usb3, USB3_DMA_INT_ENA); + if (dma_sta) { + usb3_write(usb3, dma_sta, USB3_DMA_INT_STA); + usb3_irq_dma_int(usb3, dma_sta); + } +} + static irqreturn_t renesas_usb3_irq(int irq, void *_usb3) { struct renesas_usb3 *usb3 = _usb3; irqreturn_t ret = IRQ_NONE; u32 axi_int_sta = usb3_read(usb3, USB3_AXI_INT_STA); + if (axi_int_sta & AXI_INT_DMAINT) { + usb3_irq_dma(usb3); + ret = IRQ_HANDLED; + } + if (axi_int_sta & AXI_INT_EPCINT) { usb3_irq_epc(usb3); ret = IRQ_HANDLED; @@ -1708,6 +2090,7 @@ static int renesas_usb3_ep_disable(struct usb_ep *_ep) usb3_req = usb3_get_request(usb3_ep); if (!usb3_req) break; + usb3_dma_try_stop(usb3_ep, usb3_req); usb3_request_done(usb3_ep, usb3_req, -ESHUTDOWN); } while (1); @@ -1755,6 +2138,7 @@ static int renesas_usb3_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) dev_dbg(usb3_to_dev(usb3), "ep_dequeue: ep%2d, %u\n", usb3_ep->num, _req->length); + usb3_dma_try_stop(usb3_ep, usb3_req); usb3_request_done_pipen(usb3, usb3_ep, usb3_req, -ECONNRESET); return 0; @@ -1917,6 +2301,7 @@ static int renesas_usb3_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_role); usb_del_gadget_udc(&usb3->gadget); + renesas_usb3_dma_free_prd(usb3, &pdev->dev); __renesas_usb3_ep_free_request(usb3->ep0_req); @@ -2111,6 +2496,10 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (!usb3->ep0_req) return -ENOMEM; + ret = renesas_usb3_dma_alloc_prd(usb3, &pdev->dev); + if (ret < 0) + goto err_alloc_prd; + ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget); if (ret < 0) goto err_add_udc; @@ -2129,6 +2518,9 @@ err_dev_create: usb_del_gadget_udc(&usb3->gadget); err_add_udc: + renesas_usb3_dma_free_prd(usb3, &pdev->dev); + +err_alloc_prd: __renesas_usb3_ep_free_request(usb3->ep0_req); return ret; diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/snps_udc_core.c index 4ecd2f20ea48..38a165dbf924 100644 --- a/drivers/usb/gadget/udc/amd5536udc.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -41,7 +41,6 @@ #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); static void udc_soft_reset(struct udc *dev); static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep); @@ -209,18 +208,18 @@ static void print_regs(struct udc *dev) if (use_dma && use_dma_ppb && !use_dma_ppb_du) { DBG(dev, "DMA mode = PPBNDU (packet per buffer " "WITHOUT desc. update)\n"); - dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU"); + dev_info(dev->dev, "DMA mode (%s)\n", "PPBNDU"); } else if (use_dma && use_dma_ppb && use_dma_ppb_du) { DBG(dev, "DMA mode = PPBDU (packet per buffer " "WITH desc. update)\n"); - dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU"); + dev_info(dev->dev, "DMA mode (%s)\n", "PPBDU"); } if (use_dma && use_dma_bufferfill_mode) { DBG(dev, "DMA mode = BF (buffer fill mode)\n"); - dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF"); + dev_info(dev->dev, "DMA mode (%s)\n", "BF"); } if (!use_dma) - dev_info(&dev->pdev->dev, "FIFO mode\n"); + dev_info(dev->dev, "FIFO mode\n"); DBG(dev, "-------------------------------------------------------\n"); } @@ -1244,7 +1243,7 @@ finished: } /* Empty request queue of an endpoint; caller holds spinlock */ -static void empty_req_queue(struct udc_ep *ep) +void empty_req_queue(struct udc_ep *ep) { struct udc_request *req; @@ -1256,6 +1255,7 @@ static void empty_req_queue(struct udc_ep *ep) complete_req(ep, req, -ESHUTDOWN); } } +EXPORT_SYMBOL_GPL(empty_req_queue); /* Dequeues a request packet, called by gadget driver */ static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq) @@ -1623,8 +1623,11 @@ static void udc_setup_endpoints(struct udc *dev) /* Bringup after Connect event, initial bringup to be ready for ep0 events */ static void usb_connect(struct udc *dev) { + /* Return if already connected */ + if (dev->connected) + return; - dev_info(&dev->pdev->dev, "USB Connect\n"); + dev_info(dev->dev, "USB Connect\n"); dev->connected = 1; @@ -1641,8 +1644,11 @@ static void usb_connect(struct udc *dev) */ static void usb_disconnect(struct udc *dev) { + /* Return if already disconnected */ + if (!dev->connected) + return; - dev_info(&dev->pdev->dev, "USB Disconnect\n"); + dev_info(dev->dev, "USB Disconnect\n"); dev->connected = 0; @@ -1715,11 +1721,15 @@ static void udc_soft_reset(struct udc *dev) /* device int. status reset */ writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts); - spin_lock_irqsave(&udc_irq_spinlock, flags); - writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); - readl(&dev->regs->cfg); - spin_unlock_irqrestore(&udc_irq_spinlock, flags); - + /* Don't do this for Broadcom UDC since this is a reserved + * bit. + */ + if (dev->chiprev != UDC_BCM_REV) { + spin_lock_irqsave(&udc_irq_spinlock, flags); + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + readl(&dev->regs->cfg); + spin_unlock_irqrestore(&udc_irq_spinlock, flags); + } } /* RDE timer callback to set RDE bit */ @@ -2106,7 +2116,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) } /* HE event ? */ if (tmp & AMD_BIT(UDC_EPSTS_HE)) { - dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num); + dev_err(dev->dev, "HE ep%dout occurred\n", ep->num); /* clear HE */ writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); @@ -2305,7 +2315,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) if (use_dma) { /* BNA ? */ if (epsts & AMD_BIT(UDC_EPSTS_BNA)) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "BNA ep%din occurred - DESPTR = %08lx\n", ep->num, (unsigned long) readl(&ep->regs->desptr)); @@ -2318,7 +2328,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) } /* HE event ? */ if (epsts & AMD_BIT(UDC_EPSTS_HE)) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "HE ep%dn occurred - DESPTR = %08lx\n", ep->num, (unsigned long) readl(&ep->regs->desptr)); @@ -2956,7 +2966,7 @@ __acquires(dev->lock) /* link up all endpoints */ udc_setup_endpoints(dev); - dev_info(&dev->pdev->dev, "Connect: %s\n", + dev_info(dev->dev, "Connect: %s\n", usb_speed_string(dev->gadget.speed)); /* init ep 0 */ @@ -3097,7 +3107,7 @@ int init_dma_pools(struct udc *dev) } /* DMA setup */ - dev->data_requests = dma_pool_create("data_requests", NULL, + dev->data_requests = dma_pool_create("data_requests", dev->dev, sizeof(struct udc_data_dma), 0, 0); if (!dev->data_requests) { DBG(dev, "can't get request data pool\n"); @@ -3108,7 +3118,7 @@ int init_dma_pools(struct udc *dev) dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl; /* dma desc for setup data */ - dev->stp_requests = dma_pool_create("setup requests", NULL, + dev->stp_requests = dma_pool_create("setup requests", dev->dev, sizeof(struct udc_stp_dma), 0, 0); if (!dev->stp_requests) { DBG(dev, "can't get stp request pool\n"); @@ -3168,24 +3178,30 @@ int udc_probe(struct udc *dev) /* init registers, interrupts, ... */ startup_registers(dev); - dev_info(&dev->pdev->dev, "%s\n", mod_desc); + dev_info(dev->dev, "%s\n", mod_desc); snprintf(tmp, sizeof(tmp), "%d", dev->irq); - dev_info(&dev->pdev->dev, - "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", - tmp, dev->phys_addr, dev->chiprev, - (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); - strcpy(tmp, UDC_DRIVER_VERSION_STRING); - if (dev->chiprev == UDC_HSA0_REV) { - dev_err(&dev->pdev->dev, "chip revision is A0; too old\n"); - retval = -ENODEV; - goto finished; + + /* Print this device info for AMD chips only*/ + if (dev->chiprev == UDC_HSA0_REV || + dev->chiprev == UDC_HSB1_REV) { + dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", + tmp, dev->phys_addr, dev->chiprev, + (dev->chiprev == UDC_HSA0_REV) ? + "A0" : "B1"); + strcpy(tmp, UDC_DRIVER_VERSION_STRING); + if (dev->chiprev == UDC_HSA0_REV) { + dev_err(dev->dev, "chip revision is A0; too old\n"); + retval = -ENODEV; + goto finished; + } + dev_info(dev->dev, + "driver version: %s(for Geode5536 B1)\n", tmp); } - dev_info(&dev->pdev->dev, - "driver version: %s(for Geode5536 B1)\n", tmp); + udc = dev; - retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, + retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget, gadget_release); if (retval) goto finished; diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c new file mode 100644 index 000000000000..2e11f19e07ae --- /dev/null +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -0,0 +1,344 @@ +/* + * snps_udc_plat.c - Synopsys UDC Platform Driver + * + * Copyright (C) 2016 Broadcom + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/extcon.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/module.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include "amd5536udc.h" + +/* description */ +#define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver" + +void start_udc(struct udc *udc) +{ + if (udc->driver) { + dev_info(udc->dev, "Connecting...\n"); + udc_enable_dev_setup_interrupts(udc); + udc_basic_init(udc); + udc->connected = 1; + } +} + +void stop_udc(struct udc *udc) +{ + int tmp; + u32 reg; + + spin_lock(&udc->lock); + + /* Flush the receieve fifo */ + reg = readl(&udc->regs->ctl); + reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH); + writel(reg, &udc->regs->ctl); + + reg = readl(&udc->regs->ctl); + reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH)); + writel(reg, &udc->regs->ctl); + dev_dbg(udc->dev, "ep rx queue flushed\n"); + + /* Mask interrupts. Required more so when the + * UDC is connected to a DRD phy. + */ + udc_mask_unused_interrupts(udc); + + /* Disconnect gadget driver */ + if (udc->driver) { + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + + /* empty queues */ + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) + empty_req_queue(&udc->ep[tmp]); + } + udc->connected = 0; + + spin_unlock(&udc->lock); + dev_info(udc->dev, "Device disconnected\n"); +} + +void udc_drd_work(struct work_struct *work) +{ + struct udc *udc; + + udc = container_of(to_delayed_work(work), + struct udc, drd_work); + + if (udc->conn_type) { + dev_dbg(udc->dev, "idle -> device\n"); + start_udc(udc); + } else { + dev_dbg(udc->dev, "device -> idle\n"); + stop_udc(udc); + } +} + +static int usbd_connect_notify(struct notifier_block *self, + unsigned long event, void *ptr) +{ + struct udc *udc = container_of(self, struct udc, nb); + + dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event); + + udc->conn_type = event; + + schedule_delayed_work(&udc->drd_work, 0); + + return NOTIFY_OK; +} + +static int udc_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct udc *udc; + int ret; + + udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + spin_lock_init(&udc->lock); + udc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + udc->virt_addr = devm_ioremap_resource(dev, res); + if (IS_ERR(udc->regs)) + return PTR_ERR(udc->regs); + + /* udc csr registers base */ + udc->csr = udc->virt_addr + UDC_CSR_ADDR; + + /* dev registers base */ + udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR; + + /* ep registers base */ + udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR; + + /* fifo's base */ + udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR); + udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR); + + udc->phys_addr = (unsigned long)res->start; + + udc->irq = irq_of_parse_and_map(dev->of_node, 0); + if (udc->irq <= 0) { + dev_err(dev, "Can't parse and map interrupt\n"); + return -EINVAL; + } + + udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0); + if (IS_ERR(udc->udc_phy)) { + dev_err(dev, "Failed to obtain phy from device tree\n"); + return PTR_ERR(udc->udc_phy); + } + + ret = phy_init(udc->udc_phy); + if (ret) { + dev_err(dev, "UDC phy init failed"); + return ret; + } + + ret = phy_power_on(udc->udc_phy); + if (ret) { + dev_err(dev, "UDC phy power on failed"); + phy_exit(udc->udc_phy); + return ret; + } + + /* Register for extcon if supported */ + if (of_get_property(dev->of_node, "extcon", NULL)) { + udc->edev = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(udc->edev)) { + if (PTR_ERR(udc->edev) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_err(dev, "Invalid or missing extcon\n"); + ret = PTR_ERR(udc->edev); + goto exit_phy; + } + + udc->nb.notifier_call = usbd_connect_notify; + ret = extcon_register_notifier(udc->edev, EXTCON_USB, + &udc->nb); + if (ret < 0) { + dev_err(dev, "Can't register extcon device\n"); + goto exit_phy; + } + + ret = extcon_get_cable_state_(udc->edev, EXTCON_USB); + if (ret < 0) { + dev_err(dev, "Can't get cable state\n"); + goto exit_extcon; + } else if (ret) { + udc->conn_type = ret; + } + INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work); + } + + /* init dma pools */ + if (use_dma) { + ret = init_dma_pools(udc); + if (ret != 0) + goto exit_extcon; + } + + ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED, + "snps-udc", udc); + if (ret < 0) { + dev_err(dev, "Request irq %d failed for UDC\n", udc->irq); + goto exit_dma; + } + + platform_set_drvdata(pdev, udc); + udc->chiprev = UDC_BCM_REV; + + if (udc_probe(udc)) { + ret = -ENODEV; + goto exit_dma; + } + dev_info(dev, "Synopsys UDC platform driver probe successful\n"); + + return 0; + +exit_dma: + if (use_dma) + free_dma_pools(udc); +exit_extcon: + if (udc->edev) + extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb); +exit_phy: + if (udc->udc_phy) { + phy_power_off(udc->udc_phy); + phy_exit(udc->udc_phy); + } + return ret; +} + +static int udc_plat_remove(struct platform_device *pdev) +{ + struct udc *dev; + + dev = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&dev->gadget); + /* gadget driver must not be registered */ + if (WARN_ON(dev->driver)) + return 0; + + /* dma pool cleanup */ + free_dma_pools(dev); + + udc_remove(dev); + + platform_set_drvdata(pdev, NULL); + + if (dev->drd_wq) { + flush_workqueue(dev->drd_wq); + destroy_workqueue(dev->drd_wq); + } + + phy_power_off(dev->udc_phy); + phy_exit(dev->udc_phy); + extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb); + + dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int udc_plat_suspend(struct device *dev) +{ + struct udc *udc; + + udc = dev_get_drvdata(dev); + stop_udc(udc); + + if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) { + dev_dbg(udc->dev, "device -> idle\n"); + stop_udc(udc); + } + phy_power_off(udc->udc_phy); + phy_exit(udc->udc_phy); + + return 0; +} + +static int udc_plat_resume(struct device *dev) +{ + struct udc *udc; + int ret; + + udc = dev_get_drvdata(dev); + + ret = phy_init(udc->udc_phy); + if (ret) { + dev_err(udc->dev, "UDC phy init failure"); + return ret; + } + + ret = phy_power_on(udc->udc_phy); + if (ret) { + dev_err(udc->dev, "UDC phy power on failure"); + phy_exit(udc->udc_phy); + return ret; + } + + if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) { + dev_dbg(udc->dev, "idle -> device\n"); + start_udc(udc); + } + + return 0; +} +static const struct dev_pm_ops udc_plat_pm_ops = { + .suspend = udc_plat_suspend, + .resume = udc_plat_resume, +}; +#endif + +#if defined(CONFIG_OF) +static const struct of_device_id of_udc_match[] = { + { .compatible = "brcm,ns2-udc", }, + { .compatible = "brcm,cygnus-udc", }, + { .compatible = "brcm,iproc-udc", }, + { } +}; +MODULE_DEVICE_TABLE(of, of_udc_match); +#endif + +static struct platform_driver udc_plat_driver = { + .probe = udc_plat_probe, + .remove = udc_plat_remove, + .driver = { + .name = "snps-udc-plat", + .of_match_table = of_match_ptr(of_udc_match), +#ifdef CONFIG_PM_SLEEP + .pm = &udc_plat_pm_ops, +#endif + }, +}; +module_platform_driver(udc_plat_driver); + +MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); +MODULE_AUTHOR("Broadcom"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 588e2531b8b8..de207a90571e 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -1151,7 +1151,7 @@ static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) break; } if (&req->usb_req != _req) { - spin_unlock_irqrestore(&ep->udc->lock, flags); + spin_unlock_irqrestore(&udc->lock, flags); return -EINVAL; } xudc_done(ep, req, -ECONNRESET); |