summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/udc
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-06-20 11:39:34 +0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-06-20 11:39:34 +0800
commit24040a58379e2f2fa6aa9466911b758073b6bdfa (patch)
tree772a93302d0e2e0f2aa333bb10a060e2e886c645 /drivers/usb/gadget/udc
parent571949a40a9c00fdaa3d0256cf43c9e0c25ff2d1 (diff)
parent0591bc2360152f851e29246884805bb77a2c3b9d (diff)
Merge tag 'usb-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
Felipe writes: usb: changes for v4.13 merge window This time around we have a total of 57 non-merge commits. A list of most important changes follows: - Improvements to dwc3 tracing interface - Initial dual-role support for dwc3 - Improvements to how we handle DMA resources in dwc3 - A new f_uac1 implementation which much more flexible - Removal of AVR32 bits - Improvements to f_mass_storage driver
Diffstat (limited to 'drivers/usb/gadget/udc')
-rw-r--r--drivers/usb/gadget/udc/Kconfig18
-rw-r--r--drivers/usb/gadget/udc/Makefile3
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.h18
-rw-r--r--drivers/usb/gadget/udc/amd5536udc_pci.c1
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c4
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h27
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c2
-rw-r--r--drivers/usb/gadget/udc/core.c37
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c39
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c9
-rw-r--r--drivers/usb/gadget/udc/net2280.c5
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c392
-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.c344
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c2
15 files changed, 896 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 a03aec574f64..e6f04eee95c4 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -140,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;
@@ -1067,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
*
@@ -1299,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;
@@ -1451,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) \
@@ -1486,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);