summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig8
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c122
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c19
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/fec_imx.c5
-rw-r--r--drivers/net/netx_eth.c279
-rw-r--r--drivers/nvmem/ocotp.c19
-rw-r--r--drivers/pinctrl/Kconfig10
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c320
-rw-r--r--drivers/pinctrl/pinctrl-at91.c2
-rw-r--r--drivers/serial/Kconfig10
-rw-r--r--drivers/serial/Makefile2
-rw-r--r--drivers/serial/serial_blackfin.c132
-rw-r--r--drivers/serial/serial_netx.c161
-rw-r--r--drivers/usb/dwc3/Kconfig29
-rw-r--r--drivers/usb/dwc3/Makefile7
-rw-r--r--drivers/usb/dwc3/core.c450
-rw-r--r--drivers/usb/dwc3/core.h181
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c122
-rw-r--r--drivers/usb/dwc3/ep0.c1183
-rw-r--r--drivers/usb/dwc3/gadget.c3015
-rw-r--r--drivers/usb/dwc3/gadget.h127
-rw-r--r--drivers/usb/gadget/Kconfig1
-rw-r--r--drivers/usb/gadget/at91_udc.c6
-rw-r--r--drivers/usb/gadget/composite.c43
-rw-r--r--drivers/usb/gadget/epautoconf.c19
-rw-r--r--drivers/usb/gadget/udc-core.c15
-rw-r--r--drivers/usb/musb/musb_core.c9
-rw-r--r--drivers/usb/musb/musb_core.h32
-rw-r--r--drivers/usb/musb/musb_dma.h11
-rw-r--r--drivers/usb/musb/musb_io.h24
-rw-r--r--drivers/usb/musb/musb_regs.h191
-rw-r--r--drivers/video/imx.c23
36 files changed, 5661 insertions, 927 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7a1503198b..0f924d135f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -67,6 +67,14 @@ config GPIO_MALTA_FPGA_I2C
additional drivers must be enabled in order to use the
functionality of the device.
+config GPIO_MPC8XXX
+ bool "MPC512x/MPC8xxx/QorIQ GPIO support"
+ depends on ARCH_LAYERSCAPE
+ select GPIO_GENERIC
+ help
+ Say Y here if you're going to use hardware that connects to the
+ MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs.
+
config GPIO_OMAP
def_bool ARCH_OMAP
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 990df01788..bc5c500e4d 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_GPIO_LIBFTDI1) += gpio-libftdi1.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_GPIO_JZ4740) += gpio-jz4740.o
obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o
+obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
obj-$(CONFIG_GPIO_ORION) += gpio-orion.o
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
new file mode 100644
index 0000000000..979f92ad30
--- /dev/null
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -0,0 +1,122 @@
+/*
+ * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible
+ *
+ * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
+ * Copyright (C) 2016 Freescale Semiconductor Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <gpio.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/clk.h>
+#include <malloc.h>
+#include <of.h>
+#include <of_address.h>
+#include <of_device.h>
+
+#define MPC8XXX_GPIO_PINS 32
+
+#define GPIO_DIR 0x00
+#define GPIO_ODR 0x04
+#define GPIO_DAT 0x08
+#define GPIO_IER 0x0c
+#define GPIO_IMR 0x10
+
+struct mpc8xxx_gpio_chip {
+ struct bgpio_chip bgc;
+ void __iomem *regs;
+};
+
+struct mpc8xxx_gpio_devtype {
+ int (*gpio_dir_out)(struct bgpio_chip *, unsigned int, int);
+ int (*gpio_get)(struct bgpio_chip *, unsigned int);
+};
+
+static int mpc8xxx_probe(struct device_d *dev)
+{
+ struct device_node *np;
+ struct resource *iores;
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc;
+ struct bgpio_chip *bgc;
+ int ret;
+
+ mpc8xxx_gc = xzalloc(sizeof(*mpc8xxx_gc));
+
+ if (dev->device_node) {
+ np = dev->device_node;
+ } else {
+ dev_err(dev, "no device_node\n");
+ return -ENODEV;
+ }
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ mpc8xxx_gc->regs = IOMEM(iores->start);
+ if (!mpc8xxx_gc->regs)
+ return -ENOMEM;
+
+ bgc = &mpc8xxx_gc->bgc;
+
+ if (of_property_read_bool(np, "little-endian")) {
+ ret = bgpio_init(bgc, dev, 4,
+ mpc8xxx_gc->regs + GPIO_DAT,
+ NULL, NULL,
+ mpc8xxx_gc->regs + GPIO_DIR, NULL, 0);
+ if (ret)
+ goto err;
+ dev_dbg(dev, "GPIO registers are LITTLE endian\n");
+ } else {
+ ret = bgpio_init(bgc, dev, 4,
+ mpc8xxx_gc->regs + GPIO_DAT,
+ NULL, NULL,
+ mpc8xxx_gc->regs + GPIO_DIR, NULL,
+ BGPIOF_BIG_ENDIAN);
+ if (ret)
+ goto err;
+ dev_dbg(dev, "GPIO registers are BIG endian\n");
+ }
+
+ ret = gpiochip_add(&mpc8xxx_gc->bgc.gc);
+ if (ret) {
+ pr_err("%pOF: GPIO chip registration failed with status %d\n",
+ np, ret);
+ goto err;
+ }
+
+ /* ack and mask all irqs */
+ bgc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
+ bgc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static __maybe_unused struct of_device_id mpc8xxx_gpio_ids[] = {
+ {
+ .compatible = "fsl,qoriq-gpio",
+ },
+ {
+ /* sentinel */
+ },
+};
+
+static struct driver_d mpc8xxx_driver = {
+ .name = "mpc8xxx-gpio",
+ .probe = mpc8xxx_probe,
+ .of_compatible = DRV_OF_COMPAT(mpc8xxx_gpio_ids),
+};
+
+static int __init mpc8xxx_init(void)
+{
+ return platform_driver_register(&mpc8xxx_driver);
+}
+postcore_initcall(mpc8xxx_init);
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 42bde5ed1c..aa7dcb8c31 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -170,6 +170,16 @@ static int pca954x_select_chan(struct i2c_adapter *adap,
return ret;
}
+static int pca954x_deselect_chan(struct i2c_adapter *adap,
+ void *client, u32 chan)
+{
+ struct pca954x *data = i2c_get_clientdata(client);
+
+ /* Deselect active channel */
+ data->last_chan = 0;
+ return pca954x_reg_write(adap, client, data->last_chan);
+}
+
/*
* I2C init/probing/exit functions
*/
@@ -182,6 +192,7 @@ static int pca954x_probe(struct device_d *dev)
uintptr_t tmp;
int ret = -ENODEV;
int gpio;
+ bool idle_disconnect;
data = kzalloc(sizeof(struct pca954x), GFP_KERNEL);
if (!data) {
@@ -209,6 +220,9 @@ static int pca954x_probe(struct device_d *dev)
if (ret)
goto exit_free;
+ idle_disconnect = of_property_read_bool(dev->device_node,
+ "i2c-mux-idle-disconnect");
+
data->last_chan = 0; /* force the first selection */
/* Now create an adapter for each channel */
@@ -216,7 +230,10 @@ static int pca954x_probe(struct device_d *dev)
data->virt_adaps[num] =
i2c_add_mux_adapter(adap, &client->dev, client,
- 0, num, pca954x_select_chan, NULL);
+ 0, num, pca954x_select_chan,
+ idle_disconnect ?
+ pca954x_deselect_chan :
+ NULL);
if (data->virt_adaps[num] == NULL) {
ret = -ENODEV;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index beeb4b8221..57f0b57d64 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -16,9 +16,6 @@ config HAS_DM9000
config HAS_MACB
bool
-config HAS_NETX_ETHER
- bool
-
config PHYLIB
bool
@@ -180,11 +177,6 @@ config DRIVER_NET_MVNETA
select PHYLIB
select MDIO_MVEBU
-config DRIVER_NET_NETX
- bool "Hilscher Netx ethernet driver"
- depends on HAS_NETX_ETHER
- select PHYLIB
-
config DRIVER_NET_ORION
bool "Marvell Orion SoC Ethernet"
depends on ARCH_MVEBU
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 6ccd22cc10..f6a8213613 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -24,7 +24,6 @@ obj-$(CONFIG_DRIVER_NET_MACB) += macb.o
obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o
obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o
obj-$(CONFIG_DRIVER_NET_MVNETA) += mvneta.o
-obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o
obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o
obj-$(CONFIG_DRIVER_NET_RTL8139) += rtl8139.o
obj-$(CONFIG_DRIVER_NET_RTL8169) += rtl8169.o
diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c
index 31c9102189..5ef1d4359e 100644
--- a/drivers/net/fec_imx.c
+++ b/drivers/net/fec_imx.c
@@ -781,7 +781,8 @@ static int fec_probe(struct device_d *dev)
if (IS_ERR(fec->reg_phy)) {
if (PTR_ERR(fec->reg_phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
- goto disable_clk;
+ fec->reg_phy = NULL;
+ goto release_res;
}
fec->reg_phy = NULL;
}
@@ -789,7 +790,7 @@ static int fec_probe(struct device_d *dev)
ret = regulator_enable(fec->reg_phy);
if (ret) {
dev_err(dev, "Failed to enable phy regulator: %d\n", ret);
- goto disable_clk;
+ goto release_res;
}
phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0);
diff --git a/drivers/net/netx_eth.c b/drivers/net/netx_eth.c
deleted file mode 100644
index 64e9886d61..0000000000
--- a/drivers/net/netx_eth.c
+++ /dev/null
@@ -1,279 +0,0 @@
-#include <common.h>
-#include <command.h>
-#include <net.h>
-#include <io.h>
-#include <mach/netx-xc.h>
-#include <mach/netx-eth.h>
-#include <mach/netx-regs.h>
-#include <xfuncs.h>
-#include <init.h>
-#include <driver.h>
-#include <linux/phy.h>
-
-#define ETH_MAC_LOCAL_CONFIG 0x1560
-#define ETH_MAC_4321 0x1564
-#define ETH_MAC_65 0x1568
-
-#define MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT 16
-#define MAC_TRAFFIC_CLASS_ARRANGEMENT_MASK (0xf<<MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT)
-#define MAC_TRAFFIC_CLASS_ARRANGEMENT(x) (((x)<<MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT) & MAC_TRAFFIC_CLASS_ARRANGEMENT_MASK)
-
-#define FIFO_PTR_FRAMELEN_SHIFT 0
-#define FIFO_PTR_FRAMELEN_MASK (0x7ff << 0)
-#define FIFO_PTR_FRAMELEN(len) (((len) << 0) & FIFO_PTR_FRAMELEN_MASK)
-#define FIFO_PTR_TIMETRIG (1<<11)
-#define FIFO_PTR_MULTI_REQ
-#define FIFO_PTR_ORIGIN (1<<14)
-#define FIFO_PTR_VLAN (1<<15)
-#define FIFO_PTR_FRAMENO_SHIFT 16
-#define FIFO_PTR_FRAMENO_MASK (0x3f << 16)
-#define FIFO_PTR_FRAMENO(no) ( ((no) << 16) & FIFO_PTR_FRAMENO_MASK)
-#define FIFO_PTR_SEGMENT_SHIFT 22
-#define FIFO_PTR_SEGMENT_MASK (0xf << 22)
-#define FIFO_PTR_SEGMENT(seg) (((seg) & 0xf) << 22)
-#define FIFO_PTR_ERROR_SHIFT 28
-#define FIFO_PTR_ERROR_MASK (0xf << 28)
-
-/* use sram 0 for now */
-#define SRAM_BASE(xcno) (0x8000 * (xcno))
-
-/* XC Fifo Offsets */
-#define EMPTY_PTR_FIFO(xcno) (0 + ((xcno) << 3)) /* Index of the empty pointer FIFO */
-#define IND_FIFO_PORT_HI(xcno) (1 + ((xcno) << 3)) /* Index of the FIFO where received Data packages are indicated by XC */
-#define IND_FIFO_PORT_LO(xcno) (2 + ((xcno) << 3)) /* Index of the FIFO where received Data packages are indicated by XC */
-#define REQ_FIFO_PORT_HI(xcno) (3 + ((xcno) << 3)) /* Index of the FIFO where Data packages have to be indicated by ARM which shall be sent */
-#define REQ_FIFO_PORT_LO(xcno) (4 + ((xcno) << 3)) /* Index of the FIFO where Data packages have to be indicated by ARM which shall be sent */
-#define CON_FIFO_PORT_HI(xcno) (5 + ((xcno) << 3)) /* Index of the FIFO where sent Data packages are confirmed */
-#define CON_FIFO_PORT_LO(xcno) (6 + ((xcno) << 3)) /* Index of the FIFO where sent Data packages are confirmed */
-
-struct netx_eth_priv {
- struct mii_bus miibus;
- int xcno;
-};
-
-static int netx_eth_send (struct eth_device *edev,
- void *packet, int length)
-{
- struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv;
- int xcno = priv->xcno;
- unsigned int val;
- int timeout = 500;
- unsigned char *dst = (unsigned char *)(SRAM_BASE(xcno) + 1560);
-
- memcpy(dst, (void *)packet, length);
-
- if( length < 60 ) {
- memset(dst + length, 0, 60 - length);
- length = 60;
- }
-
- PFIFO_REG(PFIFO_BASE(REQ_FIFO_PORT_LO(xcno))) =
- FIFO_PTR_SEGMENT(xcno) |
- FIFO_PTR_FRAMENO(1) |
- FIFO_PTR_FRAMELEN(length);
-
- while (!PFIFO_REG( PFIFO_FILL_LEVEL(CON_FIFO_PORT_LO(xcno))) && timeout) {
- timeout--;
- udelay(100);
- }
-#if 0
- if (!timeout) {
- loadxc(0);
- loadxc(1);
- eth_init(gd->bd);
- return -1;
- }
-#endif
- val = PFIFO_REG( PFIFO_BASE(CON_FIFO_PORT_LO(xcno)) );
- if((val & FIFO_PTR_ERROR_MASK) & 0x8)
- printf("error sending frame: %u\n", val);
-
- return 0;
-}
-
-static int netx_eth_rx (struct eth_device *edev)
-{
- struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv;
- int xcno = priv->xcno;
- unsigned int val, frameno, seg, len;
-
- if(!PFIFO_REG( PFIFO_FILL_LEVEL(IND_FIFO_PORT_LO(xcno)))) {
- return 0;
- }
-
- val = PFIFO_REG( PFIFO_BASE(IND_FIFO_PORT_LO(xcno)) );
-
- frameno = (val & FIFO_PTR_FRAMENO_MASK) >> FIFO_PTR_FRAMENO_SHIFT;
- seg = (val & FIFO_PTR_SEGMENT_MASK) >> FIFO_PTR_SEGMENT_SHIFT;
- len = (val & FIFO_PTR_FRAMELEN_MASK) >> FIFO_PTR_FRAMELEN_SHIFT;
-
- /* get data */
- memcpy((void*)NetRxPackets[0], (void *)(SRAM_BASE(seg) + frameno * 1560), len);
- /* pass to barebox */
- net_receive(edev, NetRxPackets[0], len);
-
- PFIFO_REG(PFIFO_BASE(EMPTY_PTR_FIFO(xcno))) =
- FIFO_PTR_SEGMENT(seg) |
- FIFO_PTR_FRAMENO(frameno);
- return 0;
-}
-
-static int netx_miibus_read(struct mii_bus *bus, int phy_addr, int reg)
-{
- int value;
-
- MIIMU_REG = MIIMU_SNRDY | MIIMU_PREAMBLE | MIIMU_PHYADDR(phy_addr) |
- MIIMU_REGADDR(reg) | MIIMU_PHY_NRES;
-
- while(MIIMU_REG & MIIMU_SNRDY);
-
- value = MIIMU_REG >> 16;
-
- debug("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n", __func__,
- phy_addr, reg, value);
-
- return value;
-}
-
-static int netx_miibus_write(struct mii_bus *bus, int phy_addr,
- int reg, u16 val)
-{
- debug("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n",__func__,
- phy_addr, reg, val);
-
- MIIMU_REG = MIIMU_SNRDY | MIIMU_PREAMBLE | MIIMU_PHYADDR(phy_addr) |
- MIIMU_REGADDR(reg) | MIIMU_PHY_NRES | MIIMU_OPMODE_WRITE |
- MIIMU_DATA(val);
-
- while(MIIMU_REG & MIIMU_SNRDY);
-
- return 0;
-}
-
-static int netx_eth_init_phy(void)
-{
- unsigned int phy_control;
-
- phy_control = PHY_CONTROL_PHY_ADDRESS(0xe) |
- PHY_CONTROL_PHY1_MODE(PHY_MODE_ALL) |
- PHY_CONTROL_PHY1_AUTOMDIX |
- PHY_CONTROL_PHY1_EN |
- PHY_CONTROL_PHY0_MODE(PHY_MODE_ALL) |
- PHY_CONTROL_PHY0_AUTOMDIX |
- PHY_CONTROL_PHY0_EN |
- PHY_CONTROL_CLK_XLATIN;
-
- /* enable asic control */
- SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY) = SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY);
-
- SYSTEM_REG(SYSTEM_PHY_CONTROL) = phy_control | PHY_CONTROL_RESET;
- udelay(100);
-
- /* enable asic control */
- SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY) = SYSTEM_REG(SYSTEM_IOC_ACCESS_KEY);
-
- SYSTEM_REG(SYSTEM_PHY_CONTROL) = phy_control;
-
- return 0;
-}
-
-static int netx_eth_init_dev(struct eth_device *edev)
-{
- struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv;
- int xcno = priv->xcno;
- int i;
-
- loadxc(xcno);
-
- /* Fill empty pointer fifo */
- for (i = 2; i <= 18; i++)
- PFIFO_REG( PFIFO_BASE(EMPTY_PTR_FIFO(xcno)) ) = FIFO_PTR_FRAMENO(i) | FIFO_PTR_SEGMENT(xcno);
-
- return 0;
-}
-
-static int netx_eth_open(struct eth_device *edev)
-{
- struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv;
-
- return phy_device_connect(edev, &priv->miibus, 0, NULL,
- 0, PHY_INTERFACE_MODE_NA);
-}
-
-static void netx_eth_halt (struct eth_device *edev)
-{
-}
-
-static int netx_eth_get_ethaddr(struct eth_device *edev, unsigned char *adr)
-{
- /* FIXME: get from crypto flash */
- return -1;
-}
-
-static int netx_eth_set_ethaddr(struct eth_device *edev, const unsigned char *adr)
-{
- struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv;
- int xcno = priv->xcno;
-
- debug("%s\n", __func__);
-
- /* set MAC address */
- XMAC_REG(xcno, XMAC_RPU_HOLD_PC) = RPU_HOLD_PC;
- XMAC_REG(xcno, XMAC_TPU_HOLD_PC) = TPU_HOLD_PC;
- XPEC_REG(xcno, XPEC_XPU_HOLD_PC) = XPU_HOLD_PC;
- XPEC_REG(xcno, XPEC_RAM_START + ETH_MAC_4321) = adr[0] | adr[1]<<8 | adr[2]<<16 | adr[3]<<24;
- XPEC_REG(xcno, XPEC_RAM_START + ETH_MAC_65) = adr[4] | adr[5]<<8;
- XPEC_REG(xcno, XPEC_RAM_START + ETH_MAC_LOCAL_CONFIG) = MAC_TRAFFIC_CLASS_ARRANGEMENT(8);
- XMAC_REG(xcno, XMAC_RPU_HOLD_PC) = 0;
- XMAC_REG(xcno, XMAC_TPU_HOLD_PC) = 0;
- XPEC_REG(xcno, XPEC_XPU_HOLD_PC) = 0;
-
-#if 0
- for (i = 0; i < 5; i++)
- printf ("%02x:", adr[i]);
- printf ("%02x\n", adr[5]);
-#endif
- return -0;
-}
-
-static int netx_eth_probe(struct device_d *dev)
-{
- struct eth_device *edev;
- struct netx_eth_priv *priv;
- struct netx_eth_platform_data *pdata;
-
- debug("netx_eth_probe()\n");
-
- pdata = dev->platform_data;
-
- edev = xzalloc(sizeof(struct eth_device) + sizeof(struct netx_eth_priv));
- edev->priv = (struct netx_priv *)(edev + 1);
-
- priv = edev->priv;
- priv->xcno = pdata->xcno;
-
- edev->init = netx_eth_init_dev;
- edev->open = netx_eth_open;
- edev->send = netx_eth_send;
- edev->recv = netx_eth_rx;
- edev->halt = netx_eth_halt;
- edev->get_ethaddr = netx_eth_get_ethaddr;
- edev->set_ethaddr = netx_eth_set_ethaddr;
- edev->parent = dev;
-
- priv->miibus.read = netx_miibus_read;
- priv->miibus.write = netx_miibus_write;
- priv->miibus.parent = dev;
-
- netx_eth_init_phy();
- mdiobus_register(&priv->miibus);
- eth_register(edev);
-
- return 0;
-}
-
-static struct driver_d netx_eth_driver = {
- .name = "netx-eth",
- .probe = netx_eth_probe,
-};
-device_platform_driver(netx_eth_driver);
diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c
index 3f9f162860..79693e22e0 100644
--- a/drivers/nvmem/ocotp.c
+++ b/drivers/nvmem/ocotp.c
@@ -29,6 +29,8 @@
#include <regmap.h>
#include <linux/clk.h>
#include <mach/ocotp.h>
+#include <machine_id.h>
+#include <mach/ocotp-fusemap.h>
#include <linux/nvmem-provider.h>
/*
@@ -77,6 +79,7 @@
#define MAC_OFFSET_1 (0x24 * 4)
#define MAX_MAC_OFFSETS 2
#define MAC_BYTES 8
+#define UNIQUE_ID_NUM 2
enum imx_ocotp_format_mac_direction {
OCOTP_HW_TO_MAC,
@@ -548,6 +551,19 @@ static int imx_ocotp_read(struct device_d *dev, const int offset, void *val,
return regmap_bulk_read(priv->map, offset, val, bytes);
}
+static void imx_ocotp_set_unique_machine_id(void)
+{
+ uint32_t unique_id_parts[UNIQUE_ID_NUM];
+ int i;
+
+ for (i = 0; i < UNIQUE_ID_NUM; i++)
+ if (imx_ocotp_read_field(OCOTP_UNIQUE_ID(i),
+ &unique_id_parts[i]))
+ return;
+
+ machine_id_set_hashable(unique_id_parts, sizeof(unique_id_parts));
+}
+
static const struct nvmem_bus imx_ocotp_nvmem_bus = {
.write = imx_ocotp_write,
.read = imx_ocotp_read,
@@ -633,6 +649,9 @@ static int imx_ocotp_probe(struct device_d *dev)
ethaddr->value, ethaddr);
}
+ if (IS_ENABLED(CONFIG_MACHINE_ID))
+ imx_ocotp_set_unique_machine_id();
+
imx_ocotp_init_dt(priv);
dev_add_param_bool(&(priv->dev), "sense_enable", NULL, NULL, &priv->sense_enable, priv);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index e2fb0af756..95c6708f4a 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -17,6 +17,16 @@ config PINCTRL_AT91
help
The pinmux controller found on AT91 SoCs.
+config PINCTRL_AT91PIO4
+ bool "AT91 PIO4 pinctrl driver"
+ depends on OFDEVICE
+ depends on ARCH_AT91
+ select GPIOLIB
+ select OF_GPIO
+ help
+ Say Y here to enable the at91 pinctrl/gpio driver for Atmel PIO4
+ controller available on sama5d2 SoC.
+
config PINCTRL_BCM283X
bool "GPIO and pinmux support for BCM283X"
depends on ARCH_BCM283X
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index e311df7103..e7d8ad8f4b 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
+obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o
obj-$(CONFIG_PINCTRL_BCM283X) += pinctrl-bcm2835.o
obj-pbl-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
new file mode 100644
index 0000000000..9bc259f84c
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sama5d2 pin control and GPIO chip driver
+ * Copyright (c) 2019 Ahmad Fatoum, Pengutronix
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <pinctrl.h>
+#include <malloc.h>
+#include <gpio.h>
+#include <mach/gpio.h>
+#include <linux/clk.h>
+
+#include <dt-bindings/pinctrl/at91.h>
+
+#define ATMEL_GET_PIN_NO(pinfunc) ((pinfunc) & 0xff)
+#define ATMEL_GET_PIN_FUNC(pinfunc) ((pinfunc >> 16) & 0xf)
+#define ATMEL_GET_PIN_IOSET(pinfunc) ((pinfunc >> 20) & 0xf)
+
+struct pinctrl_at91_pio4 {
+ void __iomem *base;
+ struct pinctrl_device pinctrl;
+ struct gpio_chip gpiochip;
+};
+
+struct at91_pinctrl_data {
+ unsigned nbanks;
+};
+
+static inline void __iomem *pin_to_pio4(struct pinctrl_device *pdev,
+ unsigned int *pin)
+{
+ void __iomem *pio;
+ struct pinctrl_at91_pio4 *pinctrl =
+ container_of(pdev, struct pinctrl_at91_pio4, pinctrl);
+
+ pio = pinctrl->base + (*pin / 32) * 0x40;
+ *pin %= 32;
+
+ return pio;
+}
+
+static int __pinctrl_at91_pio4_set_state(struct pinctrl_device *pdev,
+ struct device_node *np)
+{
+ u32 drive_strength, enable = 0, disable = ~0;
+ int output = -1;
+
+ int npins, i;
+ int ret;
+
+ ret = of_property_read_u32(np, "drive-strength", &drive_strength);
+ if (!ret && ATMEL_PIO_DRVSTR_LO <= drive_strength
+ && drive_strength <= ATMEL_PIO_DRVSTR_HI) {
+ disable &= ~PIO4_DRVSTR_MASK;
+ enable |= drive_strength << PIO4_DRVSTR_OFFSET;
+ }
+
+ if (of_get_property(np, "bias-disable", NULL)) {
+ disable &= ~PIO4_PUEN_MASK;
+ disable &= ~PIO4_PDEN_MASK;
+ }
+
+ if (of_get_property(np, "bias-pull-up", NULL)) {
+ enable |= PIO4_PUEN_MASK;
+ disable &= ~PIO4_PDEN_MASK;
+ }
+
+ if (of_get_property(np, "bias-pull-down", NULL)) {
+ enable |= PIO4_PDEN_MASK;
+ disable &= ~PIO4_PUEN_MASK;
+ }
+
+ if (of_get_property(np, "drive-open-drain", NULL))
+ enable |= PIO4_OPD_MASK;
+
+ if (of_get_property(np, "input-schmitt-enable", NULL))
+ enable |= PIO4_SCHMITT_MASK;
+
+ if (of_get_property(np, "input-enable", NULL))
+ disable &= ~PIO4_DIR_MASK;
+
+ if (of_get_property(np, "output-enable", NULL))
+ enable |= PIO4_DIR_MASK;
+
+ if (of_get_property(np, "output-low", NULL))
+ output = GPIOF_OUT_INIT_LOW;
+
+ if (of_get_property(np, "output-high", NULL))
+ output = GPIOF_OUT_INIT_HIGH;
+
+ of_get_property(np, "pinmux", &npins);
+ npins /= sizeof(__be32);
+
+ if (!npins) {
+ dev_err(pdev->dev, "Invalid pinmux property in %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < npins; i++) {
+ void __iomem *pio;
+ u32 conf, no, func, cfgr;
+
+ ret = of_property_read_u32_index(np, "pinmux", i, &conf);
+ if (ret)
+ return ret;
+
+ no = ATMEL_GET_PIN_NO(conf);
+ func = ATMEL_GET_PIN_FUNC(conf);
+
+ pio = pin_to_pio4(pdev, &no);
+
+ if (output == GPIOF_OUT_INIT_HIGH)
+ at91_mux_gpio4_set(pio, BIT(no), true);
+
+ if (output == GPIOF_OUT_INIT_LOW)
+ at91_mux_gpio4_set(pio, BIT(no), false);
+
+ writel(BIT(no), pio + PIO4_MSKR);
+ cfgr = readl(pio + PIO4_CFGR);
+ cfgr &= disable;
+ cfgr |= enable;
+ writel(func | cfgr, pio + PIO4_CFGR);
+ }
+
+ return 0;
+}
+
+static int pinctrl_at91_pio4_set_state(struct pinctrl_device *pdev,
+ struct device_node *np)
+{
+ struct device_node *child;
+ void *prop;
+ int ret;
+
+ prop = of_find_property(np, "pinmux", NULL);
+ if (prop)
+ return __pinctrl_at91_pio4_set_state(pdev, np);
+
+ for_each_child_of_node(np, child) {
+ ret = __pinctrl_at91_pio4_set_state(pdev, child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline void __iomem *pin_to_gpio4(struct gpio_chip *chip, unsigned int *pin)
+{
+ void __iomem *gpio;
+ struct pinctrl_at91_pio4 *pinctrl =
+ container_of(chip, struct pinctrl_at91_pio4, gpiochip);
+
+ gpio = pinctrl->base + (*pin / 32) * 0x40;
+ *pin %= 32;
+
+ return gpio;
+}
+
+static int at91_gpio4_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *gpio = pin_to_gpio4(chip, &offset);
+
+ at91_mux_gpio4_input(gpio, BIT(offset), true);
+
+ return 0;
+}
+
+static int at91_gpio4_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ void __iomem *gpio = pin_to_gpio4(chip, &offset);
+
+ at91_mux_gpio4_set(gpio, BIT(offset), value);
+ at91_mux_gpio4_input(gpio, BIT(offset), false);
+
+ return 0;
+}
+
+static int at91_gpio4_request(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *gpio = pin_to_gpio4(chip, &offset);
+
+ at91_mux_gpio4_enable(gpio, BIT(offset));
+
+ return 0;
+}
+
+static int at91_gpio4_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ u32 cfgr;
+ void __iomem *gpio = pin_to_gpio4(chip, &offset);
+
+ writel(BIT(offset), gpio + PIO4_MSKR);
+ cfgr = readl(gpio + PIO4_CFGR);
+
+ return cfgr & PIO4_DIR_MASK ? GPIOF_DIR_OUT : GPIOF_DIR_IN;
+}
+
+static void at91_gpio4_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ void __iomem *gpio = pin_to_gpio4(chip, &offset);
+
+ at91_mux_gpio4_set(gpio, BIT(offset), value);
+}
+
+static int at91_gpio4_get(struct gpio_chip *chip, unsigned offset)
+{
+ void __iomem *gpio = pin_to_gpio4(chip, &offset);
+
+ return at91_mux_gpio4_get(gpio, BIT(offset));
+}
+
+static struct gpio_ops at91_gpio4_ops = {
+ .request = at91_gpio4_request,
+ .direction_input = at91_gpio4_direction_input,
+ .direction_output = at91_gpio4_direction_output,
+ .get_direction = at91_gpio4_get_direction,
+ .get = at91_gpio4_get,
+ .set = at91_gpio4_set,
+};
+
+static int pinctrl_at91_pio4_gpiochip_add(struct device_d *dev,
+ struct pinctrl_at91_pio4 *pinctrl)
+{
+ struct at91_pinctrl_data *drvdata;
+ struct clk *clk;
+ int ret;
+
+ clk = clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "clock not found: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(clk);
+ if (ret < 0) {
+ dev_err(dev, "clock failed to enable: %d\n", ret);
+ clk_put(clk);
+ return ret;
+ }
+
+ dev_get_drvdata(dev, (const void **)&drvdata);
+
+ pinctrl->gpiochip.ops = &at91_gpio4_ops;
+ pinctrl->gpiochip.base = 0;
+ pinctrl->gpiochip.ngpio = drvdata->nbanks * MAX_NB_GPIO_PER_BANK;
+ pinctrl->gpiochip.dev = dev;
+
+ ret = gpiochip_add(&pinctrl->gpiochip);
+ if (ret) {
+ dev_err(dev, "couldn't add gpiochip, ret = %d\n", ret);
+ return ret;
+ }
+
+ dev_info(dev, "gpio driver registered\n");
+
+ return 0;
+}
+
+static struct pinctrl_ops pinctrl_at91_pio4_ops = {
+ .set_state = pinctrl_at91_pio4_set_state,
+};
+
+static int pinctrl_at91_pio4_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct pinctrl_at91_pio4 *pinctrl;
+ struct resource *io;
+ int ret;
+
+ pinctrl = xzalloc(sizeof(*pinctrl));
+
+ io = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(io))
+ return PTR_ERR(io);
+
+ pinctrl->base = IOMEM(io->start);
+ pinctrl->pinctrl.dev = dev;
+ pinctrl->pinctrl.ops = &pinctrl_at91_pio4_ops;
+
+ ret = pinctrl_register(&pinctrl->pinctrl);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "pinctrl driver registered\n");
+
+ if (of_get_property(np, "gpio-controller", NULL))
+ return pinctrl_at91_pio4_gpiochip_add(dev, pinctrl);
+
+ return 0;
+}
+
+static const struct at91_pinctrl_data sama5d2_pinctrl_data = {
+ .nbanks = 4,
+};
+
+static __maybe_unused struct of_device_id pinctrl_at91_pio4_dt_ids[] = {
+ { .compatible = "atmel,sama5d2-pinctrl", .data = &sama5d2_pinctrl_data },
+ { /* sentinel */ }
+};
+
+static struct driver_d pinctrl_at91_pio4_driver = {
+ .name = "pinctrl-at91-pio4",
+ .probe = pinctrl_at91_pio4_probe,
+ .of_compatible = DRV_OF_COMPAT(pinctrl_at91_pio4_dt_ids),
+};
+
+static int pinctrl_at91_pio4_init(void)
+{
+ return platform_driver_register(&pinctrl_at91_pio4_driver);
+}
+core_initcall(pinctrl_at91_pio4_init);
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 9b366e4812..0da6332720 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -152,6 +152,8 @@ int at91_mux_pin(unsigned pin, enum at91_mux mux, int use_pullup)
return -EINVAL;
at91_gpio->ops->mux_D_periph(pio, mask);
break;
+ default:
+ return -EINVAL;
}
if (mux)
at91_mux_gpio_disable(pio, mask);
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index f12ff93f6a..7a411d456e 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -50,11 +50,6 @@ config DRIVER_SERIAL_AUART
depends on ARCH_MXS
bool "i.MX23/i.MX28 application UART serial driver"
-config DRIVER_SERIAL_NETX
- depends on ARCH_NETX
- default y
- bool "Netx serial driver"
-
config DRIVER_SERIAL_LINUX_CONSOLE
depends on LINUX
default y
@@ -69,11 +64,6 @@ config DRIVER_SERIAL_MPC5XXX
default y
bool "MPC5200 serial driver"
-config DRIVER_SERIAL_BLACKFIN
- depends on BLACKFIN
- default y
- bool "Blackfin serial driver"
-
config DRIVER_SERIAL_CLPS711X
depends on ARCH_CLPS711X
default y
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 4174cc1ffb..6f9e3b7835 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -5,10 +5,8 @@ obj-$(CONFIG_DRIVER_SERIAL_EFI) += serial_efi.o
obj-$(CONFIG_DRIVER_SERIAL_IMX) += serial_imx.o
obj-$(CONFIG_DRIVER_SERIAL_STM378X) += stm-serial.o
obj-$(CONFIG_DRIVER_SERIAL_ATMEL) += atmel.o
-obj-$(CONFIG_DRIVER_SERIAL_NETX) += serial_netx.o
obj-$(CONFIG_DRIVER_SERIAL_LINUX_CONSOLE) += linux_console.o
obj-$(CONFIG_DRIVER_SERIAL_MPC5XXX) += serial_mpc5xxx.o
-obj-$(CONFIG_DRIVER_SERIAL_BLACKFIN) += serial_blackfin.o
obj-$(CONFIG_DRIVER_SERIAL_CLPS711X) += serial_clps711x.o
obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o
obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o
diff --git a/drivers/serial/serial_blackfin.c b/drivers/serial/serial_blackfin.c
deleted file mode 100644
index 2122226734..0000000000
--- a/drivers/serial/serial_blackfin.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * (C) Copyright 2005
- * Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <common.h>
-#include <driver.h>
-#include <init.h>
-#include <malloc.h>
-#include <io.h>
-#include <asm/blackfin.h>
-
-#define UART_IER_ERBFI 0x01
-#define UART_IER_ETBEI 0x02
-#define UART_IER_ELSI 0x04
-#define UART_IER_EDDSI 0x08
-
-#define UART_IIR_NOINT 0x01
-#define UART_IIR_STATUS 0x06
-#define UART_IIR_LSR 0x06
-#define UART_IIR_RBR 0x04
-#define UART_IIR_THR 0x02
-#define UART_IIR_MSR 0x00
-
-#define UART_LCR_WLS5 0
-#define UART_LCR_WLS6 0x01
-#define UART_LCR_WLS7 0x02
-#define UART_LCR_WLS8 0x03
-#define UART_LCR_STB 0x04
-#define UART_LCR_PEN 0x08
-#define UART_LCR_EPS 0x10
-#define UART_LCR_SP 0x20
-#define UART_LCR_SB 0x40
-#define UART_LCR_DLAB 0x80
-
-#define UART_LSR_DR 0x01
-#define UART_LSR_OE 0x02
-#define UART_LSR_PE 0x04
-#define UART_LSR_FE 0x08
-#define UART_LSR_BI 0x10
-#define UART_LSR_THRE 0x20
-#define UART_LSR_TEMT 0x40
-
-#define UART_GCTL_UCEN 0x01
-
-static int blackfin_serial_setbaudrate(struct console_device *cdev, int baudrate)
-{
- int divisor, oldlcr;
-
- oldlcr = readw(UART_LCR);
-
- divisor = (get_sclk() + (baudrate * 0)) / (baudrate * 16);
-
- /* Set DLAB in LCR to Access DLL and DLH */
- writew(UART_LCR_DLAB, UART_LCR);
-
- writew(divisor & 0xff, UART_DLL);
- writew((divisor >> 8) & 0xff, UART_DLH);
-
- /* Clear DLAB in LCR to Access THR RBR IER */
- writew(oldlcr, UART_LCR);
-
- return 0;
-}
-
-static int blackfin_serial_init_port(struct console_device *cdev)
-{
- /* Enable UART */
- writew(UART_GCTL_UCEN, UART_GCTL);
-
- /* Set LCR to Word Lengh 8-bit word select */
- writew(UART_LCR_WLS8, UART_LCR);
-
- return 0;
-}
-
-static void blackfin_serial_putc(struct console_device *cdev, char c)
-{
- while (!(readw(UART_LSR) & UART_LSR_TEMT));
-
- writew(c, UART_THR);
-}
-
-static int blackfin_serial_getc(struct console_device *cdev)
-{
- while (!(readw(UART_LSR) & UART_LSR_DR));
-
- return readw(UART_RBR);
-}
-
-static int blackfin_serial_tstc(struct console_device *cdev)
-{
- return (readw(UART_LSR) & UART_LSR_DR) ? 1 : 0;
-}
-
-static int blackfin_serial_probe(struct device_d *dev)
-{
- struct console_device *cdev;
-
- cdev = xzalloc(sizeof(struct console_device));
- cdev->dev = dev;
- cdev->tstc = blackfin_serial_tstc;
- cdev->putc = blackfin_serial_putc;
- cdev->getc = blackfin_serial_getc;
- cdev->setbrg = blackfin_serial_setbaudrate;
-
- blackfin_serial_init_port(cdev);
-
- console_register(cdev);
-
- return 0;
-}
-
-static struct driver_d blackfin_serial_driver = {
- .name = "blackfin_serial",
- .probe = blackfin_serial_probe,
-};
-console_platform_driver(blackfin_serial_driver);
diff --git a/drivers/serial/serial_netx.c b/drivers/serial/serial_netx.c
deleted file mode 100644
index 55ed89bf92..0000000000
--- a/drivers/serial/serial_netx.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * (C) Copyright 2005
- * Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <common.h>
-#include <mach/netx-regs.h>
-#include <driver.h>
-#include <init.h>
-#include <malloc.h>
-#include <io.h>
-
-enum uart_regs {
- UART_DR = 0x00,
- UART_SR = 0x04,
- UART_LINE_CR = 0x08,
- UART_BAUDDIV_MSB = 0x0c,
- UART_BAUDDIV_LSB = 0x10,
- UART_CR = 0x14,
- UART_FR = 0x18,
- UART_IIR = 0x1c,
- UART_ILPR = 0x20,
- UART_RTS_CR = 0x24,
- UART_RTS_LEAD = 0x28,
- UART_RTS_TRAIL = 0x2c,
- UART_DRV_ENABLE = 0x30,
- UART_BRM_CR = 0x34,
- UART_RXFIFO_IRQLEVEL = 0x38,
- UART_TXFIFO_IRQLEVEL = 0x3c,
-};
-
-#define LINE_CR_5BIT (0<<5)
-#define LINE_CR_6BIT (1<<5)
-#define LINE_CR_7BIT (2<<5)
-#define LINE_CR_8BIT (3<<5)
-#define LINE_CR_FEN (1<<4)
-
-#define CR_UARTEN (1<<0)
-
-#define FR_TXFE (1<<7)
-#define FR_RXFF (1<<6)
-#define FR_TXFF (1<<5)
-#define FR_RXFE (1<<4)
-#define FR_BUSY (1<<3)
-#define FR_DCD (1<<2)
-#define FR_DSR (1<<1)
-#define FR_CTS (1<<0)
-
-#define DRV_ENABLE_TX (1<<1)
-#define DRV_ENABLE_RTS (1<<0)
-
-#define BRM_CR_BAUD_RATE_MODE (1<<0)
-
-static int netx_serial_init_port(struct console_device *cdev)
-{
- struct device_d *dev = cdev->dev;
- void __iomem *base = dev->priv;
- unsigned int divisor;
-
- /* disable uart */
- writel(0, base + UART_CR);
- writel(BRM_CR_BAUD_RATE_MODE, base + UART_BRM_CR);
-
- /* set baud rate */
- divisor = 115200 * 4096;
- divisor /= 1000;
- divisor *= 256;
- divisor /= 100000;
-
- writel(divisor & 0xff, base + UART_BAUDDIV_LSB);
- writel((divisor >> 8) & 0xff, base + UART_BAUDDIV_MSB);
- writel(DRV_ENABLE_TX | DRV_ENABLE_RTS, base + UART_DRV_ENABLE);
-
- writel(LINE_CR_8BIT | LINE_CR_FEN, base + UART_LINE_CR);
-
- /* Finally, enable the UART */
- writel(CR_UARTEN, base + UART_CR);
-
- return 0;
-}
-
-static int netx_serial_setbaudrate(struct console_device *cdev, int baudrate)
-{
- return 0;
-}
-
-static void netx_serial_putc(struct console_device *cdev, char c)
-{
- struct device_d *dev = cdev->dev;
- void __iomem *base = dev->priv;
-
- while (readl(base + UART_FR) & FR_TXFF );
-
- writel(c, base + UART_DR);
-}
-
-static int netx_serial_getc(struct console_device *cdev)
-{
- struct device_d *dev = cdev->dev;
- void __iomem *base = dev->priv;
- int c;
-
- while (readl(base + UART_FR) & FR_RXFE );
-
- c = readl(base + UART_DR);
-
- readl(base + UART_SR);
-
- return c;
-}
-
-static int netx_serial_tstc(struct console_device *cdev)
-{
- struct device_d *dev = cdev->dev;
- void __iomem *base = dev->priv;
-
- return (readl(base + UART_FR) & FR_RXFE) ? 0 : 1;
-}
-
-static int netx_serial_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct console_device *cdev;
-
- cdev = xzalloc(sizeof(struct console_device));
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- dev->priv = IOMEM(iores->start);
- cdev->dev = dev;
- cdev->tstc = netx_serial_tstc;
- cdev->putc = netx_serial_putc;
- cdev->getc = netx_serial_getc;
- cdev->setbrg = netx_serial_setbaudrate;
-
- netx_serial_init_port(cdev);
-
- console_register(cdev);
-
- return 0;
-}
-
-static struct driver_d netx_serial_driver = {
- .name = "netx_serial",
- .probe = netx_serial_probe,
-};
-console_platform_driver(netx_serial_driver);
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index c91fd36a95..09da121374 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -2,8 +2,6 @@ config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on USB && HAS_DMA
select USB_XHCI
- select USB_DWC3_HOST # Remove this once we support more
- # than USB host
help
Say Y or M here if your system has a Dual Role SuperSpeed
USB controller based on the DesignWare USB3 IP Core.
@@ -13,10 +11,37 @@ config USB_DWC3
if USB_DWC3
+choice
+ bool "DWC3 Mode Selection"
+
config USB_DWC3_HOST
bool "Host only mode"
help
Select this when you want to use DWC3 in host mode only,
thereby the gadget feature will be regressed.
+config USB_DWC3_GADGET
+ bool "Gadget only mode"
+ depends on USB_GADGET
+ help
+ Select this when you want to use DWC3 in gadget mode only,
+ thereby the host feature will be regressed.
+
+config USB_DWC3_DUAL_ROLE
+ bool "Dual Role mode"
+ help
+ This is the default mode of working of DWC3 controller where
+ both host and gadget features are enabled.
+
+endchoice
+
+config USB_DWC3_OF_SIMPLE
+ tristate "Generic OF Simple Glue Layer"
+ depends on COMMON_CLK
+ default USB_DWC3
+ help
+ Support USB2/3 functionality in simple SoC integrations.
+ Currently supports Xilinx and Qualcomm DWC USB3 IP.
+ Say 'Y' or 'M' if you have one such device.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index d43b23eb2d..d0c812c883 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -4,7 +4,12 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
-ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST)),)
+ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += host.o
endif
+ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
+ dwc3-y += gadget.o ep0.o
+endif
+
+obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 60fd6318db..d3f9d9ef27 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -11,15 +11,77 @@
#include <common.h>
#include <linux/clk.h>
#include <linux/phy/phy.h>
+#include <dma.h>
#include <driver.h>
#include <init.h>
+#include "gadget.h"
#include "core.h"
#include "io.h"
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
+/**
+ * dwc3_get_dr_mode - Validates and sets dr_mode
+ * @dwc: pointer to our context structure
+ */
+static int dwc3_get_dr_mode(struct dwc3 *dwc)
+{
+ enum usb_dr_mode mode;
+ struct device_d *dev = dwc->dev;
+ unsigned int hw_mode;
+
+ if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+ dwc->dr_mode = USB_DR_MODE_OTG;
+
+ mode = dwc->dr_mode;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
+ switch (hw_mode) {
+ case DWC3_GHWPARAMS0_MODE_GADGET:
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
+ dev_err(dev,
+ "Controller does not support host mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_PERIPHERAL;
+ break;
+ case DWC3_GHWPARAMS0_MODE_HOST:
+ if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
+ dev_err(dev,
+ "Controller does not support device mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_HOST;
+ break;
+ default:
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ mode = USB_DR_MODE_PERIPHERAL;
+
+ /*
+ * DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG
+ * mode. If the controller supports DRD but the dr_mode is not
+ * specified or set to OTG, then set the mode to peripheral.
+ */
+ if (mode == USB_DR_MODE_OTG &&
+ dwc->revision >= DWC3_REVISION_330A)
+ mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ if (mode != dwc->dr_mode) {
+ dev_warn(dev,
+ "Configuration mismatch. dr_mode forced to %s\n",
+ mode == USB_DR_MODE_HOST ? "host" : "gadget");
+
+ dwc->dr_mode = mode;
+ }
+
+ return 0;
+}
+
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -89,6 +151,180 @@ done:
return 0;
}
+/**
+ * dwc3_free_one_event_buffer - Frees one event buffer
+ * @dwc: Pointer to our controller context structure
+ * @evt: Pointer to event buffer to be freed
+ */
+static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
+ struct dwc3_event_buffer *evt)
+{
+ dma_free_coherent(evt->buf, 0, sizeof(dma_addr_t));
+}
+
+/**
+ * dwc3_alloc_one_event_buffer - Allocates one event buffer structure
+ * @dwc: Pointer to our controller context structure
+ * @length: size of the event buffer
+ *
+ * Returns a pointer to the allocated event buffer structure on success
+ * otherwise ERR_PTR(errno).
+ */
+static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
+ unsigned length)
+{
+ struct dwc3_event_buffer *evt;
+
+ evt = xzalloc(sizeof(*evt));
+ if (!evt)
+ return ERR_PTR(-ENOMEM);
+
+ evt->dwc = dwc;
+ evt->length = length;
+ evt->buf = dma_alloc_coherent(length, &evt->dma);
+ if (!evt->buf)
+ return ERR_PTR(-ENOMEM);
+
+ return evt;
+}
+
+/**
+ * dwc3_free_event_buffers - frees all allocated event buffers
+ * @dwc: Pointer to our controller context structure
+ */
+static void dwc3_free_event_buffers(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+
+ evt = dwc->ev_buf;
+ if (evt)
+ dwc3_free_one_event_buffer(dwc, evt);
+}
+
+/**
+ * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
+ * @dwc: pointer to our controller context structure
+ * @length: size of event buffer
+ *
+ * Returns 0 on success otherwise negative errno. In the error case, dwc
+ * may contain some buffers allocated but not all which were requested.
+ */
+static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
+{
+ struct dwc3_event_buffer *evt;
+
+ evt = dwc3_alloc_one_event_buffer(dwc, length);
+ if (IS_ERR(evt)) {
+ dev_err(dwc->dev, "can't allocate event buffer\n");
+ return PTR_ERR(evt);
+ }
+ dwc->ev_buf = evt;
+
+ return 0;
+}
+
+/**
+ * dwc3_event_buffers_setup - setup our allocated event buffers
+ * @dwc: pointer to our controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+
+ evt = dwc->ev_buf;
+ evt->lpos = 0;
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
+ lower_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
+ upper_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
+
+ return 0;
+}
+
+
+static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+
+ evt = dwc->ev_buf;
+
+ evt->lpos = 0;
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
+ | DWC3_GEVNTSIZ_SIZE(0));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
+}
+
+static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
+{
+ if (!dwc->has_hibernation)
+ return 0;
+
+ if (!dwc->nr_scratch)
+ return 0;
+
+ dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
+ DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
+ if (!dwc->scratchbuf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
+{
+ dma_addr_t scratch_addr;
+ u32 param;
+ int ret;
+
+ if (!dwc->has_hibernation)
+ return 0;
+
+ if (!dwc->nr_scratch)
+ return 0;
+
+ scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+ dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dwc->dev, scratch_addr)) {
+ dev_err(dwc->dev, "failed to map scratch buffer\n");
+ ret = -EFAULT;
+ goto err0;
+ }
+
+ dwc->scratch_addr = scratch_addr;
+
+ param = lower_32_bits(scratch_addr);
+
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
+ if (ret < 0)
+ goto err1;
+
+ param = upper_32_bits(scratch_addr);
+
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
+ if (ret < 0)
+ goto err1;
+
+ return 0;
+
+err1:
+ dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+
+err0:
+ return ret;
+}
+
static const struct clk_bulk_data dwc3_core_clks[] = {
{ .id = "ref" },
{ .id = "bus_early" },
@@ -142,6 +378,22 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
}
+static int dwc3_core_ulpi_init(struct dwc3 *dwc)
+{
+ int intf;
+ int ret = 0;
+
+ intf = DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3);
+
+ if (intf == DWC3_GHWPARAMS3_HSPHY_IFC_ULPI ||
+ (intf == DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI &&
+ dwc->hsphy_interface &&
+ !strncmp(dwc->hsphy_interface, "ulpi", 4)))
+ ret = dwc3_ulpi_init(dwc);
+
+ return ret;
+}
+
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -228,6 +480,23 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
break;
}
+ switch (dwc->hsphy_mode) {
+ case USBPHY_INTERFACE_MODE_UTMI:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
+ break;
+ case USBPHY_INTERFACE_MODE_UTMIW:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
+ break;
+ default:
+ break;
+ }
+
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
@@ -255,12 +524,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
static void dwc3_core_exit(struct dwc3 *dwc)
{
+ dwc3_event_buffers_cleanup(dwc);
+
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
clk_bulk_disable(dwc->num_clks, dwc->clks);
+
+ dwc3_free_event_buffers(dwc);
}
static bool dwc3_core_is_valid(struct dwc3 *dwc)
@@ -361,6 +634,105 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
static int dwc3_core_get_phy(struct dwc3 *dwc);
+/* set global incr burst type configuration registers */
+static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
+{
+ struct device_d *dev = dwc->dev;
+ /* incrx_mode : for INCR burst type. */
+ bool incrx_mode;
+ /* incrx_size : for size of INCRX burst. */
+ u32 incrx_size;
+ u32 *vals;
+ u32 cfg;
+ int ntype = 0;
+ int ret;
+ int i;
+
+ cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+
+ /*
+ * Handle property "snps,incr-burst-type-adjustment".
+ * Get the number of value from this property:
+ * result <= 0, means this property is not supported.
+ * result = 1, means INCRx burst mode supported.
+ * result > 1, means undefined length burst mode supported.
+ */
+ of_find_property(dev->device_node, "snps,incr-burst-type-adjustment",
+ &ntype);
+
+ ntype /= sizeof(u32);
+
+ if (ntype <= 0)
+ return;
+
+ vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL);
+ if (!vals) {
+ dev_err(dev, "Error to get memory\n");
+ return;
+ }
+
+ /* Get INCR burst type, and parse it */
+ ret = of_property_read_u32_array(dev->device_node,
+ "snps,incr-burst-type-adjustment",
+ vals, ntype);
+ if (ret) {
+ kfree(vals);
+ dev_err(dev, "Error to get property\n");
+ return;
+ }
+
+ incrx_size = *vals;
+
+ if (ntype > 1) {
+ /* INCRX (undefined length) burst mode */
+ incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE;
+ for (i = 1; i < ntype; i++) {
+ if (vals[i] > incrx_size)
+ incrx_size = vals[i];
+ }
+ } else {
+ /* INCRX burst mode */
+ incrx_mode = INCRX_BURST_MODE;
+ }
+
+ kfree(vals);
+
+ /* Enable Undefined Length INCR Burst and Enable INCRx Burst */
+ cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK;
+ if (incrx_mode)
+ cfg |= DWC3_GSBUSCFG0_INCRBRSTENA;
+ switch (incrx_size) {
+ case 256:
+ cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA;
+ break;
+ case 128:
+ cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA;
+ break;
+ case 64:
+ cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA;
+ break;
+ case 32:
+ cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA;
+ break;
+ case 16:
+ cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA;
+ break;
+ case 8:
+ cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA;
+ break;
+ case 4:
+ cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA;
+ break;
+ case 1:
+ break;
+ default:
+ dev_err(dev, "Invalid property\n");
+ break;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
+}
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -395,6 +767,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err0;
+ if (!dwc->ulpi_ready) {
+ ret = dwc3_core_ulpi_init(dwc);
+ if (ret)
+ goto err0;
+ dwc->ulpi_ready = true;
+ }
+
if (!dwc->phys_ready) {
ret = dwc3_core_get_phy(dwc);
if (ret)
@@ -409,16 +788,29 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
+ ret = dwc3_setup_scratch_buffers(dwc);
+ if (ret)
+ goto err0a;
+
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc);
+ dwc3_set_incr_burst_type(dwc);
+
ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0)
- goto err2;
+ goto err1;
ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0)
+ goto err2;
+
+ ret = dwc3_event_buffers_setup(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
goto err3;
+ }
+
/*
* ENDXFER polling is available on version 3.10a and later of
* the DWC_usb3 controller. It is NOT available in the
@@ -501,12 +893,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
return 0;
-/* err4: */
- /* phy_power_off(dwc->usb3_generic_phy); */
err3:
- phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
err2:
-/* err1: */
+ phy_power_off(dwc->usb2_generic_phy);
+err1:
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
err0a:
@@ -556,6 +947,16 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
int ret;
switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+
+ ret = dwc3_gadget_init(dwc);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize gadget\n");
+ return ret;
+ }
+ break;
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
@@ -595,10 +996,19 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL);
dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL);
+ dwc->hsphy_mode = of_usb_get_phy_mode(dev->device_node, NULL);
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
+ if (of_get_property(dev->device_node, "snps,dis_rxdet_inp3_quirk",
+ NULL))
+ dwc->dis_rxdet_inp3_quirk = 1;
+
+ of_property_read_u32_array(dev->device_node,
+ "snps,quirk-frame-length-adjustment",
+ &dwc->fladj, 1);
+
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
@@ -699,15 +1109,13 @@ static int dwc3_probe(struct device_d *dev)
if (dev->device_node) {
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
- ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
- if (ret == -EPROBE_DEFER)
- return ret;
- /*
- * Clocks are optional, but new DT platforms should support all
- * clocks as required by the DT-binding.
- */
- if (ret)
- dwc->num_clks = 0;
+ if (of_find_property(dev->device_node, "clocks", NULL)) {
+ ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ if (ret)
+ return ret;
+ }
}
ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
@@ -718,6 +1126,20 @@ static int dwc3_probe(struct device_d *dev)
dwc3_cache_hwparams(dwc);
+ ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
+ if (ret) {
+ dev_err(dwc->dev, "failed to allocate event buffers\n");
+ return -ENOMEM;
+ }
+
+ ret = dwc3_get_dr_mode(dwc);
+ if (ret)
+ return ret;
+
+ ret = dwc3_alloc_scratch_buffers(dwc);
+ if (ret)
+ return ret;
+
ret = dwc3_core_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index a404e4cd6a..df0a188a63 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -11,7 +11,10 @@
#ifndef __DRIVERS_USB_DWC3_CORE_H
#define __DRIVERS_USB_DWC3_CORE_H
+#include <linux/spinlock.h>
#include <usb/usb.h>
+#include <usb/phy.h>
+#include <usb/gadget.h>
#define DWC3_MSG_MAX 500
@@ -364,6 +367,7 @@
#define DWC3_DCFG_HIGHSPEED (0 << 0)
#define DWC3_DCFG_FULLSPEED BIT(0)
#define DWC3_DCFG_LOWSPEED (2 << 0)
+#define DWC3_DCFG_FULLSPEED1 (3 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
@@ -457,6 +461,7 @@
#define DWC3_DSTS_HIGHSPEED (0 << 0)
#define DWC3_DSTS_FULLSPEED BIT(0)
#define DWC3_DSTS_LOWSPEED (2 << 0)
+#define DWC3_DSTS_FULLSPEED1 (3 << 0)
/* Device Generic Command Register */
#define DWC3_DGCMD_SET_LMP 0x01
@@ -594,6 +599,22 @@
struct dwc3_trb;
+/**
+ * struct dwc3_event_buffer - Software event buffer representation
+ * @buf: _THE_ buffer
+ * @length: size of this buffer
+ * @lpos: event offset
+ * @dma: dma_addr_t
+ * @dwc: pointer to DWC controller
+ */
+struct dwc3_event_buffer {
+ void *buf;
+ unsigned length;
+ unsigned int lpos;
+ dma_addr_t dma;
+ struct dwc3 *dwc;
+};
+
#define DWC3_EP_FLAG_STALLED BIT(0)
#define DWC3_EP_FLAG_WEDGED BIT(1)
@@ -601,6 +622,84 @@ struct dwc3_trb;
#define DWC3_EP_DIRECTION_RX false
#define DWC3_TRB_NUM 256
+#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1)
+
+/**
+ * struct dwc3_ep - device side endpoint representation
+ * @endpoint: usb endpoint
+ * @pending_list: list of requests for this endpoint
+ * @started_list: list of started requests on this endpoint
+ * @trb_pool: array of transaction buffers
+ * @trb_pool_dma: dma address of @trb_pool
+ * @free_slot: next slot which is going to be used
+ * @busy_slot: first slot which is owned by HW
+ * @desc: usb_endpoint_descriptor pointer
+ * @dwc: pointer to DWC controller
+ * @saved_state: ep state saved during hibernation
+ * @flags: endpoint flags (wedged, stalled, ...)
+ * @current_trb: index of current used trb
+ * @number: endpoint number (1 - 15)
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
+ * @resource_index: Resource transfer index
+ * @interval: the interval on which the ISOC transfer is started
+ * @name: a human readable name e.g. ep1out-bulk
+ * @direction: true for TX, false for RX
+ * @stream_capable: true when streams are enabled
+ */
+struct dwc3_ep {
+ struct usb_ep endpoint;
+ struct list_head cancelled_list;
+ struct list_head pending_list;
+ struct list_head started_list;
+
+ void __iomem *regs;
+
+ struct dwc3_trb *trb_pool;
+ dma_addr_t trb_pool_dma;
+ u32 free_slot;
+ u32 busy_slot;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ struct dwc3 *dwc;
+
+ u32 saved_state;
+ unsigned flags;
+#define DWC3_EP_ENABLED BIT(0)
+#define DWC3_EP_STALL BIT(1)
+#define DWC3_EP_WEDGE BIT(2)
+#define DWC3_EP_TRANSFER_STARTED BIT(3)
+#define DWC3_EP_PENDING_REQUEST BIT(4)
+
+ /* This last one is specific to EP0 */
+#define DWC3_EP0_DIR_IN BIT(31)
+
+ unsigned current_trb;
+ /*
+ * IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
+ * use a u8 type here. If anybody decides to increase number of TRBs to
+ * anything larger than 256 - I can't see why people would want to do
+ * this though - then this type needs to be changed.
+ *
+ * By using u8 types we ensure that our % operator when incrementing
+ * enqueue and dequeue get optimized away by the compiler.
+ */
+ u8 trb_enqueue;
+ u8 trb_dequeue;
+
+ u8 number;
+ u8 type;
+ u8 resource_index;
+ u32 frame_number;
+ u32 interval;
+
+ char name[20];
+
+ unsigned direction:1;
+ unsigned stream_capable:1;
+
+ /* For isochronous START TRANSFER workaround only */
+ u8 combo_num;
+ int start_cmd_status;
+};
enum dwc3_phy {
DWC3_PHY_UNKNOWN = 0,
@@ -730,6 +829,33 @@ struct dwc3_hwparams {
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
+struct dwc3_request {
+ struct usb_request request;
+ struct list_head list;
+ struct dwc3_ep *dep;
+ u32 start_slot;
+
+ unsigned remaining;
+
+ unsigned int status;
+#define DWC3_REQUEST_STATUS_QUEUED 0
+#define DWC3_REQUEST_STATUS_STARTED 1
+#define DWC3_REQUEST_STATUS_CANCELLED 2
+#define DWC3_REQUEST_STATUS_COMPLETED 3
+#define DWC3_REQUEST_STATUS_UNKNOWN -1
+
+ u8 epnum;
+ struct dwc3_trb *trb;
+ dma_addr_t trb_dma;
+
+ unsigned num_trbs;
+
+ unsigned needs_extra_trb:1;
+ unsigned direction:1;
+ unsigned mapped:1;
+ unsigned queued:1;
+};
+
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
@@ -855,9 +981,25 @@ struct dwc3_hwparams {
* increments or 0 to disable.
*/
struct dwc3 {
+ struct dwc3_trb *ep0_trb;
+ void *bounce;
+ void *scratchbuf;
+ u8 *setup_buf;
+ dma_addr_t ep0_trb_addr;
+ dma_addr_t bounce_addr;
+ dma_addr_t scratch_addr;
+ struct dwc3_request ep0_usb_req;
+
struct device_d *dev;
+
struct device_d *xhci;
+ struct dwc3_event_buffer *ev_buf;
+ struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+
struct clk_bulk_data *clks;
int num_clks;
@@ -865,12 +1007,14 @@ struct dwc3 {
struct phy *usb3_generic_phy;
bool phys_ready;
+ bool ulpi_ready;
void __iomem *regs;
enum usb_dr_mode dr_mode;
u32 current_dr_role;
u32 desired_dr_role;
+ enum usb_phy_interface hsphy_mode;
u32 fladj;
u32 irq_gadget;
@@ -934,6 +1078,11 @@ struct dwc3 {
#define DWC31_VERSIONTYPE_EA05 0x65613035
#define DWC31_VERSIONTYPE_EA06 0x65613036
+ enum dwc3_ep0_next ep0_next_event;
+ enum dwc3_ep0_state ep0state;
+ enum dwc3_link_state link_state;
+
+ u16 isoch_delay;
u16 u2sel;
u16 u2pel;
u8 u1sel;
@@ -966,10 +1115,14 @@ struct dwc3 {
unsigned sysdev_is_parent:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
+ unsigned is_selfpowered:1;
unsigned is_fpga:1;
unsigned pending_events:1;
+ unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
+ unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
+ unsigned start_config_issued:1;
unsigned three_stage_setup:1;
unsigned dis_start_transfer_quirk:1;
unsigned usb3_lpm_capable:1;
@@ -1020,6 +1173,30 @@ struct dwc3_event_type {
#define DWC3_DEPEVT_EPCMDCMPLT 0x07
/**
+ * dwc3_ep_event_string - returns event name
+ * @event: then event code
+ */
+static inline const char *dwc3_ep_event_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ return "Transfer Complete";
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ return "Transfer In-Progress";
+ case DWC3_DEPEVT_XFERNOTREADY:
+ return "Transfer Not Ready";
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ return "FIFO";
+ case DWC3_DEPEVT_STREAMEVT:
+ return "Stream";
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ return "Endpoint Command Complete";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
* struct dwc3_event_depvt - Device Endpoint Events
* @one_bit: indicates this is an endpoint event (not used)
* @endpoint_number: number of the endpoint
@@ -1157,6 +1334,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
@@ -1174,9 +1352,6 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc)
bool dwc3_has_imod(struct dwc3 *dwc);
-int dwc3_event_buffers_setup(struct dwc3 *dwc);
-void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
-
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
#else
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
new file mode 100644
index 0000000000..e58d9f95fe
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * dwc3-of-simple.c - OF glue layer for simple integrations
+ *
+ * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
+ * <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC
+ * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com>
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <of.h>
+
+struct dwc3_of_simple {
+ struct device_d *dev;
+ struct clk **clks;
+ int num_clocks;
+};
+
+static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
+{
+ struct device_d *dev = simple->dev;
+ struct device_node *np = dev->device_node;
+ int i;
+
+ simple->num_clocks = count;
+
+ if (!count)
+ return 0;
+
+ simple->clks = xzalloc(sizeof(struct clk *));
+ if (!simple->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < simple->num_clocks; i++) {
+ struct clk *clk;
+
+ clk = of_clk_get(np, i);
+ if (IS_ERR(clk)) {
+ while (--i >= 0) {
+ clk_disable(simple->clks[i]);
+ clk_put(simple->clks[i]);
+ }
+ return PTR_ERR(clk);
+ }
+
+ simple->clks[i] = clk;
+ }
+
+ return 0;
+}
+
+static int dwc3_of_simple_probe(struct device_d *dev)
+{
+ struct dwc3_of_simple *simple;
+ struct device_node *np = dev->device_node;
+
+ int ret;
+ int i;
+
+ simple = xzalloc(sizeof(*simple));
+ if (!simple)
+ return -ENOMEM;
+
+ dev->priv = simple;
+ simple->dev = dev;
+
+ ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np,
+ "clocks", "#clock-cells"));
+ if (ret)
+ return ret;
+
+ ret = of_platform_populate(np, NULL, dev);
+ if (ret) {
+ for (i = 0; i < simple->num_clocks; i++) {
+ clk_disable(simple->clks[i]);
+ clk_put(simple->clks[i]);
+ }
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dwc3_of_simple_remove(struct device_d *dev)
+{
+ struct dwc3_of_simple *simple = dev->priv;
+ int i;
+
+ for (i = 0; i < simple->num_clocks; i++) {
+ clk_disable(simple->clks[i]);
+ clk_put(simple->clks[i]);
+ }
+ simple->num_clocks = 0;
+}
+
+static const struct of_device_id of_dwc3_simple_match[] = {
+ {.compatible = "rockchip,rk3399-dwc3"},
+ {.compatible = "xlnx,zynqmp-dwc3"},
+ {.compatible = "fsl,ls1046a-dwc3"},
+ {.compatible = "cavium,octeon-7130-usb-uctl"},
+ {.compatible = "sprd,sc9860-dwc3"},
+ {.compatible = "amlogic,meson-axg-dwc3"},
+ {.compatible = "amlogic,meson-gxl-dwc3"},
+ {.compatible = "allwinner,sun50i-h6-dwc3"},
+ {/* Sentinel */}};
+
+static struct driver_d dwc3_of_simple_driver = {
+ .probe = dwc3_of_simple_probe,
+ .remove = dwc3_of_simple_remove,
+ .name = "dwc3-of-simple",
+ .of_compatible = DRV_OF_COMPAT(of_dwc3_simple_match),
+};
+device_platform_driver(dwc3_of_simple_driver);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
new file mode 100644
index 0000000000..b757a57886
--- /dev/null
+++ b/drivers/usb/dwc3/ep0.c
@@ -0,0 +1,1183 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported
+ * to uboot.
+ *
+ * commit c00552ebaf : Merge 3.18-rc7 into usb-next
+ */
+
+#include <common.h>
+#include <dma.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <usb/gadget.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep, struct dwc3_request *req);
+
+static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
+ dma_addr_t buf_dma, u32 len, u32 type,
+ bool chain)
+{
+ struct dwc3_trb *trb;
+ struct dwc3 *dwc;
+
+ dwc = dep->dwc;
+ trb = &dwc->ep0_trb[dep->trb_enqueue];
+
+ if (chain)
+ dep->trb_enqueue++;
+
+ trb->bpl = lower_32_bits(buf_dma);
+ trb->bph = upper_32_bits(buf_dma);
+ trb->size = len;
+ trb->ctrl = type;
+
+ trb->ctrl |= (DWC3_TRB_CTRL_HWO
+ | DWC3_TRB_CTRL_ISP_IMI);
+
+ if (chain)
+ trb->ctrl |= DWC3_TRB_CTRL_CHN;
+ else
+ trb->ctrl |= (DWC3_TRB_CTRL_IOC
+ | DWC3_TRB_CTRL_LST);
+}
+
+static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc;
+ int ret;
+
+ dwc = dep->dwc;
+
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
+ dev_err(dwc->dev, "%s: transfer already started\n", dep->name);
+ return 0;
+ }
+
+ memset(&params, 0, sizeof(params));
+ params.param0 = upper_32_bits(dwc->ep0_trb_addr);
+ params.param1 = lower_32_bits(dwc->ep0_trb_addr);
+
+ ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, &params);
+ if (ret < 0) {
+ dev_err(dwc->dev, "%s: STARTTRANSFER failed\n", dep->name);
+ return ret;
+ }
+
+ dwc->ep0_next_event = DWC3_EP0_COMPLETE;
+
+ return 0;
+}
+
+static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+{
+ switch (state) {
+ case EP0_UNCONNECTED:
+ return "Unconnected";
+ case EP0_SETUP_PHASE:
+ return "Setup Phase";
+ case EP0_DATA_PHASE:
+ return "Data Phase";
+ case EP0_STATUS_PHASE:
+ return "Status Phase";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
+ struct dwc3_request *req)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ req->request.actual = 0;
+ req->request.status = -EINPROGRESS;
+ req->epnum = dep->number;
+
+ list_add_tail(&req->list, &dep->pending_list);
+
+ /*
+ * Gadget driver might not be quick enough to queue a request
+ * before we get a Transfer Not Ready event on this endpoint.
+ *
+ * In that case, we will set DWC3_EP_PENDING_REQUEST. When that
+ * flag is set, it's telling us that as soon as Gadget queues the
+ * required request, we should kick the transfer here because the
+ * IRQ we were waiting for is long gone.
+ */
+ if (dep->flags & DWC3_EP_PENDING_REQUEST) {
+ unsigned direction;
+
+ direction = !!(dep->flags & DWC3_EP0_DIR_IN);
+
+ if (dwc->ep0state != EP0_DATA_PHASE) {
+ dev_warn(dwc->dev, "Unexpected pending request\n");
+ return 0;
+ }
+
+ __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
+ dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
+ DWC3_EP0_DIR_IN);
+
+ return 0;
+ }
+
+ /*
+ * In case gadget driver asked us to delay the STATUS phase,
+ * handle it here.
+ */
+ if (dwc->delayed_status) {
+ unsigned direction;
+
+ direction = !dwc->ep0_expect_in;
+ dwc->delayed_status = false;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
+
+ if (dwc->ep0state == EP0_STATUS_PHASE)
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
+ else
+ dev_dbg(dwc->dev, "too early for delayed status\n");
+
+ return 0;
+ }
+
+ /*
+ * Unfortunately we have uncovered a limitation wrt the Data Phase.
+ *
+ * Section 9.4 says we can wait for the XferNotReady(DATA) event to
+ * come before issueing Start Transfer command, but if we do, we will
+ * miss situations where the host starts another SETUP phase instead of
+ * the DATA phase. Such cases happen at least on TD.7.6 of the Link
+ * Layer Compliance Suite.
+ *
+ * The problem surfaces due to the fact that in case of back-to-back
+ * SETUP packets there will be no XferNotReady(DATA) generated and we
+ * will be stuck waiting for XferNotReady(DATA) forever.
+ *
+ * By looking at tables 9-13 and 9-14 of the Databook, we can see that
+ * it tells us to start Data Phase right away. It also mentions that if
+ * we receive a SETUP phase instead of the DATA phase, core will issue
+ * XferComplete for the DATA phase, before actually initiating it in
+ * the wire, with the TRB's status set to "SETUP_PENDING". Such status
+ * can only be used to print some debugging logs, as the core expects
+ * us to go through to the STATUS phase and start a CONTROL_STATUS TRB,
+ * just so it completes right away, without transferring anything and,
+ * only then, we can go back to the SETUP phase.
+ *
+ * Because of this scenario, SNPS decided to change the programming
+ * model of control transfers and support on-demand transfers only for
+ * the STATUS phase. To fix the issue we have now, we will always wait
+ * for gadget driver to queue the DATA phase's struct usb_request, then
+ * start it right away.
+ *
+ * If we're actually in a 2-stage transfer, we will wait for
+ * XferNotReady(STATUS).
+ */
+ if (dwc->three_stage_setup) {
+ unsigned direction;
+
+ direction = dwc->ep0_expect_in;
+ dwc->ep0state = EP0_DATA_PHASE;
+
+ __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
+ dep->flags &= ~DWC3_EP0_DIR_IN;
+ }
+
+ return 0;
+}
+
+int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (!dep->endpoint.desc) {
+ dev_err(dwc->dev, "trying to queue request %p to disabled %s\n",
+ request, dep->name);
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ /* we share one TRB for ep0/1 */
+ if (!list_empty(&dep->pending_list)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ dev_dbg(dwc->dev, "queueing request %p to %s length %d state '%s'\n",
+ request, dep->name, request->length,
+ dwc3_ep0_state_string(dwc->ep0state));
+
+ ret = __dwc3_gadget_ep0_queue(dep, req);
+
+out:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+
+ /* reinitialize physical ep1 */
+ dep = dwc->eps[1];
+ dep->flags = DWC3_EP_ENABLED;
+
+ /* stall is always issued on EP0 */
+ dep = dwc->eps[0];
+ __dwc3_gadget_ep_set_halt(dep, 1, false);
+ dep->flags = DWC3_EP_ENABLED;
+ dwc->delayed_status = false;
+
+ if (!list_empty(&dep->pending_list)) {
+ struct dwc3_request *req;
+
+ req = next_request(&dep->pending_list);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ }
+
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+}
+
+int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ dwc3_ep0_stall_and_restart(dwc);
+
+ return 0;
+}
+
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep0_set_halt(ep, value);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+void dwc3_ep0_out_start(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret;
+
+ dep = dwc->eps[0];
+ dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
+ DWC3_TRBCTL_CONTROL_SETUP, false);
+ ret = dwc3_ep0_start_trans(dep);
+ WARN_ON(ret < 0);
+}
+
+static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
+{
+ struct dwc3_ep *dep;
+ u32 windex = le16_to_cpu(wIndex_le);
+ u32 epnum;
+
+ epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1;
+ if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ epnum |= 1;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_ENABLED)
+ return dep;
+
+ return NULL;
+}
+
+static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+}
+/*
+ * ch 9.4.5
+ */
+static int dwc3_ep0_handle_status(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct dwc3_ep *dep;
+ u32 recip;
+ u32 value;
+ u32 reg;
+ u16 usb_status = 0;
+ __le16 *response_pkt;
+
+ /* We don't support PTM_STATUS */
+ value = le16_to_cpu(ctrl->wValue);
+ if (value != 0)
+ return -EINVAL;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ /*
+ * LTM will be set once we know how to set this in HW.
+ */
+ usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+
+ if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
+ (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (reg & DWC3_DCTL_INITU1ENA)
+ usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
+ if (reg & DWC3_DCTL_INITU2ENA)
+ usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
+ }
+
+ break;
+
+ case USB_RECIP_INTERFACE:
+ /*
+ * Function Remote Wake Capable D0
+ * Function Remote Wakeup D1
+ */
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
+ if (!dep)
+ return -EINVAL;
+
+ if (dep->flags & DWC3_EP_STALL)
+ usb_status = 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ response_pkt = (__le16 *) dwc->setup_buf;
+ *response_pkt = cpu_to_le16(usb_status);
+
+ dep = dwc->eps[0];
+ dwc->ep0_usb_req.dep = dep;
+ dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
+ dwc->ep0_usb_req.request.buf = dwc->setup_buf;
+ dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
+
+ return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
+}
+
+static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
+ int set)
+{
+ u32 reg;
+
+ if (state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU1ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
+ int set)
+{
+ u32 reg;
+
+ if (state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU2ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
+ u32 wIndex, int set)
+{
+ if ((wIndex & 0xff) != 0)
+ return -EINVAL;
+ if (!set)
+ return -EINVAL;
+
+ switch (wIndex >> 8) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ dwc->test_mode_nr = wIndex >> 8;
+ dwc->test_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_device(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ state = dwc->gadget.state;
+
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
+ /*
+ * 9.4.1 says only only for SS, in AddressState only for
+ * default control pipe
+ */
+ case USB_DEVICE_U1_ENABLE:
+ ret = dwc3_ep0_handle_u1(dwc, state, set);
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ ret = dwc3_ep0_handle_u2(dwc, state, set);
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ ret = -EINVAL;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ ret = dwc3_ep0_handle_test(dwc, state, wIndex, set);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ u32 wValue;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
+ /*
+ * REVISIT: Ideally we would enable some low power mode here,
+ * however it's unclear what we should be doing here.
+ *
+ * For now, we're not doing anything, just making sure we return
+ * 0 so USB Command Verifier tests pass without any errors.
+ */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ struct dwc3_ep *dep;
+ u32 wValue;
+ int ret;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ switch (wValue) {
+ case USB_ENDPOINT_HALT:
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
+ if (!dep)
+ return -EINVAL;
+
+ if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
+ break;
+
+ ret = __dwc3_gadget_ep_set_halt(dep, set, true);
+ if (ret)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ u32 recip;
+ int ret;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ ret = dwc3_ep0_handle_device(dwc, ctrl, set);
+ break;
+ case USB_RECIP_INTERFACE:
+ ret = dwc3_ep0_handle_intf(dwc, ctrl, set);
+ break;
+ case USB_RECIP_ENDPOINT:
+ ret = dwc3_ep0_handle_endpoint(dwc, ctrl, set);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ enum usb_device_state state = dwc->gadget.state;
+ u32 addr;
+ u32 reg;
+
+ addr = le16_to_cpu(ctrl->wValue);
+ if (addr > 127) {
+ dev_err(dwc->dev, "invalid device address %d\n", addr);
+ return -EINVAL;
+ }
+
+ if (state == USB_STATE_CONFIGURED) {
+ dev_err(dwc->dev, "trying to set address when configured\n");
+ return -EINVAL;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_DEVADDR_MASK);
+ reg |= DWC3_DCFG_DEVADDR(addr);
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ if (addr)
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
+ else
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+
+ return 0;
+}
+
+static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ spin_unlock(&dwc->lock);
+ ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ spin_lock(&dwc->lock);
+ return ret;
+}
+
+#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
+static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ enum usb_device_state state = dwc->gadget.state;
+ u32 cfg;
+ int ret;
+ u32 reg;
+
+ cfg = le16_to_cpu(ctrl->wValue);
+
+ switch (state) {
+ case USB_STATE_DEFAULT:
+ return -EINVAL;
+
+ case USB_STATE_ADDRESS:
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ /* if the cfg matches and the cfg is non zero */
+ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
+
+ /*
+ * only change state if set_config has already
+ * been processed. If gadget driver returns
+ * USB_GADGET_DELAYED_STATUS, we will wait
+ * to change the state on the next usb_ep_queue()
+ */
+ if (ret == 0)
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
+
+ /*
+ * Enable transition to U1/U2 state when
+ * nothing is pending from application.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
+ break;
+
+ case USB_STATE_CONFIGURED:
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ if (!cfg && !ret)
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_ADDRESS);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ u32 param = 0;
+ u32 reg;
+
+ struct timing {
+ u8 u1sel;
+ u8 u1pel;
+ __le16 u2sel;
+ __le16 u2pel;
+ } __packed timing;
+
+ int ret;
+
+ memcpy(&timing, req->buf, sizeof(timing));
+
+ dwc->u1sel = timing.u1sel;
+ dwc->u1pel = timing.u1pel;
+ dwc->u2sel = le16_to_cpu(timing.u2sel);
+ dwc->u2pel = le16_to_cpu(timing.u2pel);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (reg & DWC3_DCTL_INITU2ENA)
+ param = dwc->u2pel;
+ if (reg & DWC3_DCTL_INITU1ENA)
+ param = dwc->u1pel;
+
+ /*
+ * According to Synopsys Databook, if parameter is
+ * greater than 125, a value of zero should be
+ * programmed in the register.
+ */
+ if (param > 125)
+ param = 0;
+
+ /* now that we have the time, issue DGCMD Set Sel */
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_SET_PERIODIC_PAR, param);
+ WARN_ON(ret < 0);
+}
+
+static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ struct dwc3_ep *dep;
+ enum usb_device_state state = dwc->gadget.state;
+ u16 wLength;
+
+ if (state == USB_STATE_DEFAULT)
+ return -EINVAL;
+
+ wLength = le16_to_cpu(ctrl->wLength);
+
+ if (wLength != 6) {
+ dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n",
+ wLength);
+ return -EINVAL;
+ }
+
+ /*
+ * To handle Set SEL we need to receive 6 bytes from Host. So let's
+ * queue a usb_request for 6 bytes.
+ *
+ * Remember, though, this controller can't handle non-wMaxPacketSize
+ * aligned transfers on the OUT direction, so we queue a request for
+ * wMaxPacketSize instead.
+ */
+ dep = dwc->eps[0];
+ dwc->ep0_usb_req.dep = dep;
+ dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
+ dwc->ep0_usb_req.request.buf = dwc->setup_buf;
+ dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
+
+ return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
+}
+
+static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl)
+{
+ u16 wLength;
+ u16 wValue;
+ u16 wIndex;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wLength = le16_to_cpu(ctrl->wLength);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+
+ if (wIndex || wLength)
+ return -EINVAL;
+
+ /*
+ * REVISIT It's unclear from Databook what to do with this
+ * value. For now, just cache it.
+ */
+ dwc->isoch_delay = wValue;
+
+ return 0;
+}
+
+static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ dev_dbg(dwc->dev, "USB_REQ_GET_STATUS\n");
+ ret = dwc3_ep0_handle_status(dwc, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ dev_dbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
+ ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
+ break;
+ case USB_REQ_SET_FEATURE:
+ dev_dbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
+ ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ dev_dbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
+ ret = dwc3_ep0_set_address(dwc, ctrl);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ dev_dbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
+ ret = dwc3_ep0_set_config(dwc, ctrl);
+ break;
+ case USB_REQ_SET_SEL:
+ dev_dbg(dwc->dev, "USB_REQ_SET_SEL\n");
+ ret = dwc3_ep0_set_sel(dwc, ctrl);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ dev_dbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+ ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
+ break;
+ default:
+ dev_dbg(dwc->dev, "Forwarding to gadget driver\n");
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ break;
+ }
+
+ return ret;
+}
+
+static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb;
+ int ret = -EINVAL;
+ u32 len;
+
+ if (!dwc->gadget_driver)
+ goto out;
+
+ len = le16_to_cpu(ctrl->wLength);
+ if (!len) {
+ dwc->three_stage_setup = false;
+ dwc->ep0_expect_in = false;
+ dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
+ } else {
+ dwc->three_stage_setup = true;
+ dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN);
+ dwc->ep0_next_event = DWC3_EP0_NRDY_DATA;
+ }
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ ret = dwc3_ep0_std_request(dwc, ctrl);
+ else
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+
+ if (ret == USB_GADGET_DELAYED_STATUS)
+ dwc->delayed_status = true;
+
+out:
+ if (ret < 0)
+ dwc3_ep0_stall_and_restart(dwc);
+}
+
+static void dwc3_ep0_complete_data(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_request *r = NULL;
+ struct usb_request *ur;
+ struct dwc3_trb *trb;
+ struct dwc3_ep *ep0;
+ u32 transferred = 0;
+ u32 status;
+ u32 length;
+ u8 epnum;
+
+ epnum = event->endpoint_number;
+ ep0 = dwc->eps[0];
+
+ dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
+ trb = dwc->ep0_trb;
+
+ r = next_request(&ep0->pending_list);
+ if (!r)
+ return;
+
+ status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dev_dbg(dwc->dev, "Setup Pending received\n");
+ dwc->setup_packet_pending = true;
+
+ if (r)
+ dwc3_gadget_giveback(ep0, r, -ECONNRESET);
+
+ return;
+ }
+
+ ur = &r->request;
+
+ length = trb->size & DWC3_TRB_SIZE_MASK;
+ transferred = ur->length - length;
+ ur->actual += transferred;
+
+ if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
+ ur->length && ur->zero) || dwc->ep0_bounced) {
+ trb++;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+
+ if (r->direction)
+ dwc->eps[1]->trb_enqueue = 0;
+ else
+ dwc->eps[0]->trb_enqueue = 0;
+
+ dwc->ep0_bounced = false;
+ }
+
+ if ((epnum & 1) && ur->actual < ur->length)
+ dwc3_ep0_stall_and_restart(dwc);
+ else
+ dwc3_gadget_giveback(ep0, r, 0);
+}
+
+static void dwc3_ep0_complete_status(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_request *r;
+ struct dwc3_ep *dep;
+ struct dwc3_trb *trb;
+ u32 status;
+
+ dep = dwc->eps[0];
+ trb = dwc->ep0_trb;
+
+ if (!list_empty(&dep->pending_list)) {
+ r = next_request(&dep->pending_list);
+
+ dwc3_gadget_giveback(dep, r, 0);
+ }
+
+ if (dwc->test_mode) {
+ int ret;
+
+ ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
+ if (ret < 0) {
+ dev_dbg(dwc->dev, "Invalid Test #%d\n",
+ dwc->test_mode_nr);
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
+ }
+
+ status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dev_dbg(dwc->dev, "Setup Pending received\n");
+ dwc->setup_packet_pending = true;
+ }
+
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+}
+
+static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ dep->resource_index = 0;
+ dwc->setup_packet_pending = false;
+
+ switch (dwc->ep0state) {
+ case EP0_SETUP_PHASE:
+ dev_dbg(dwc->dev, "Setup Phase\n");
+ dwc3_ep0_inspect_setup(dwc, event);
+ break;
+
+ case EP0_DATA_PHASE:
+ dev_dbg(dwc->dev, "Data Phase\n");
+ dwc3_ep0_complete_data(dwc, event);
+ break;
+
+ case EP0_STATUS_PHASE:
+ dev_dbg(dwc->dev, "Status Phase\n");
+ dwc3_ep0_complete_status(dwc, event);
+ break;
+ default:
+ WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
+ }
+}
+
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep,
+ struct dwc3_request *req)
+{
+ dma_addr_t dma_addr;
+ int ret;
+
+ req->direction = !!dep->number;
+
+ if (req->request.length == 0) {
+ dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
+ DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dep);
+ } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
+ (dep->number == 0)) {
+ u32 maxpacket;
+ u32 rem;
+
+ dma_addr = dma_map_single(dwc->dev, req->request.buf,
+ req->request.length,
+ dep->number ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (dma_mapping_error(dwc->dev, dma_addr))
+ return;
+
+ req->request.dma = dma_addr;
+
+ maxpacket = dep->endpoint.maxpacket;
+ rem = req->request.length % maxpacket;
+ dwc->ep0_bounced = true;
+
+ /* prepare normal TRB */
+ dwc3_ep0_prepare_one_trb(dep, req->request.dma,
+ req->request.length,
+ DWC3_TRBCTL_CONTROL_DATA,
+ true);
+
+ req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
+
+ /* Now prepare one extra TRB to align transfer size */
+ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
+ maxpacket - rem,
+ DWC3_TRBCTL_CONTROL_DATA,
+ false);
+ ret = dwc3_ep0_start_trans(dep);
+ } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
+ req->request.length && req->request.zero) {
+ dma_addr = dma_map_single(dwc->dev, req->request.buf,
+ req->request.length,
+ dep->number ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (dma_mapping_error(dwc->dev, dma_addr))
+ return;
+
+ req->request.dma = dma_addr;
+
+ /* prepare normal TRB */
+ dwc3_ep0_prepare_one_trb(dep, req->request.dma,
+ req->request.length,
+ DWC3_TRBCTL_CONTROL_DATA,
+ true);
+
+ req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
+
+ /* Now prepare one extra TRB to align transfer size */
+ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA,
+ false);
+ ret = dwc3_ep0_start_trans(dep);
+ } else {
+ dma_addr = dma_map_single(dwc->dev, req->request.buf,
+ req->request.length,
+ dep->number ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (dma_mapping_error(dwc->dev, dma_addr))
+ return;
+
+ req->request.dma = dma_addr;
+
+ dwc3_ep0_prepare_one_trb(dep, req->request.dma,
+ req->request.length, DWC3_TRBCTL_CONTROL_DATA,
+ false);
+
+ req->trb = &dwc->ep0_trb[dep->trb_enqueue];
+
+ ret = dwc3_ep0_start_trans(dep);
+ }
+
+ WARN_ON(ret < 0);
+}
+
+static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 type;
+
+ type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
+ : DWC3_TRBCTL_CONTROL_STATUS2;
+
+ dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dep);
+}
+
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ WARN_ON(dwc3_ep0_start_control_status(dep));
+}
+
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+
+ __dwc3_ep0_do_control_status(dwc, dep);
+}
+
+static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+ if (!dep->resource_index)
+ return;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ WARN_ON(ret);
+ dep->resource_index = 0;
+}
+
+static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ switch (event->status) {
+ case DEPEVT_STATUS_CONTROL_DATA:
+ dev_dbg(dwc->dev, "Control Data\n");
+
+ /*
+ * We already have a DATA transfer in the controller's cache,
+ * if we receive a XferNotReady(DATA) we will ignore it, unless
+ * it's for the wrong direction.
+ *
+ * In that case, we must issue END_TRANSFER command to the Data
+ * Phase we already have started and issue SetStall on the
+ * control endpoint.
+ */
+ if (dwc->ep0_expect_in != event->endpoint_number) {
+ struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
+
+ dev_dbg(dwc->dev, "Wrong direction for Data phase\n");
+ dwc3_ep0_end_control_data(dwc, dep);
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
+
+ break;
+
+ case DEPEVT_STATUS_CONTROL_STATUS:
+ if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
+ return;
+
+ dev_dbg(dwc->dev, "Control Status\n");
+
+ dwc->ep0state = EP0_STATUS_PHASE;
+
+ if (dwc->delayed_status) {
+ struct dwc3_ep *dep = dwc->eps[0];
+
+ WARN_ON(event->endpoint_number != 1);
+ dev_dbg(dwc->dev, "Delayed Status\n");
+ /*
+ * We should handle the delay STATUS phase here if the
+ * request for handling delay STATUS has been queued
+ * into the list.
+ */
+ if (!list_empty(&dep->pending_list)) {
+ dwc->delayed_status = false;
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
+ dwc3_ep0_do_control_status(dwc, event);
+ }
+
+ return;
+ }
+
+ dwc3_ep0_do_control_status(dwc, event);
+ }
+}
+
+void dwc3_ep0_interrupt(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ u8 epnum = event->endpoint_number;
+
+ dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
+ dwc3_ep_event_string(event->endpoint_event),
+ epnum >> 1, (epnum & 1) ? "in" : "out",
+ dwc3_ep0_state_string(dwc->ep0state));
+
+ switch (event->endpoint_event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ dwc3_ep0_xfer_complete(dwc, event);
+ break;
+
+ case DWC3_DEPEVT_XFERNOTREADY:
+ dwc3_ep0_xfernotready(dwc, event);
+ break;
+
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ case DWC3_DEPEVT_STREAMEVT:
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ break;
+ }
+}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
new file mode 100644
index 0000000000..f416acc999
--- /dev/null
+++ b/drivers/usb/dwc3/gadget.c
@@ -0,0 +1,3015 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported
+ * to uboot.
+ *
+ * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier
+ */
+#include <common.h>
+#include <dma.h>
+#include <io.h>
+#include <linux/list.h>
+
+#include <usb/gadget.h>
+#include <usb/ch9.h>
+
+#include "core.h"
+#include "gadget.h"
+
+#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
+ & ~((d)->interval - 1))
+
+
+/**
+ * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
+ * @dwc: pointer to our context structure
+ * @mode: the mode to set (J, K SE0 NAK, Force Enable)
+ *
+ * Caller should take care of locking. This function will
+ * return 0 on success or -EINVAL if wrong Test Selector
+ * is passed
+ */
+int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_TSTCTRL_MASK;
+
+ switch (mode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ reg |= mode << 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+/**
+ * dwc3_gadget_get_link_state - Gets current state of USB Link
+ * @dwc: pointer to our context structure
+ *
+ * Caller should take care of locking. This function will
+ * return the link state on success (>= 0) or -ETIMEDOUT.
+ */
+int dwc3_gadget_get_link_state(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ return DWC3_DSTS_USBLNKST(reg);
+}
+
+/**
+ * dwc3_gadget_set_link_state - Sets USB Link to a particular State
+ * @dwc: pointer to our context structure
+ * @state: the state to put link into
+ *
+ * Caller should take care of locking. This function will
+ * return 0 on success or -ETIMEDOUT.
+ */
+int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
+{
+ int retries = 10000;
+ u32 reg;
+
+ /*
+ * Wait until device controller is ready. Only applies to 1.94a and
+ * later RTL.
+ */
+ if (dwc->revision >= DWC3_REVISION_194A) {
+ while (--retries) {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ if (reg & DWC3_DSTS_DCNRD)
+ udelay(5);
+ else
+ break;
+ }
+
+ if (retries <= 0)
+ return -ETIMEDOUT;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+
+ /* set requested state */
+ reg |= DWC3_DCTL_ULSTCHNGREQ(state);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ /*
+ * The following code is racy when called from dwc3_gadget_wakeup,
+ * and is not needed, at least on newer versions
+ */
+ if (dwc->revision >= DWC3_REVISION_194A)
+ return 0;
+
+ /* wait for a change in DSTS */
+ retries = 10000;
+ while (--retries) {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ if (DWC3_DSTS_USBLNKST(reg) == state)
+ return 0;
+
+ udelay(5);
+ }
+
+ dev_dbg(dwc->dev, "link state change request timed out\n");
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * dwc3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
+ *
+ * The index should never point to the link TRB. After incrementing,
+ * if it is point to the link TRB, wrap around to the beginning. The
+ * link TRB is always at the last TRB entry.
+ */
+static void dwc3_ep_inc_trb(u8 *index)
+{
+ (*index)++;
+ if (*index == (DWC3_TRB_NUM - 1))
+ *index = 0;
+}
+
+/**
+ * dwc3_ep_inc_enq - increment endpoint's enqueue pointer
+ * @dep: The endpoint whose enqueue pointer we're incrementing
+ */
+static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
+{
+ dwc3_ep_inc_trb(&dep->trb_enqueue);
+}
+
+/**
+ * dwc3_ep_inc_deq - increment endpoint's dequeue pointer
+ * @dep: The endpoint whose enqueue pointer we're incrementing
+ */
+static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
+{
+ dwc3_ep_inc_trb(&dep->trb_dequeue);
+}
+
+static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
+ struct dwc3_request *req,
+ int status)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ list_del(&req->list);
+ req->remaining = 0;
+ req->needs_extra_trb = false;
+
+ if (req->request.status == -EINPROGRESS)
+ req->request.status = status;
+
+ if (req->request.length == 0)
+ return;
+
+ if (req->trb)
+ dma_unmap_single(dwc->dev, req->request.dma,
+ req->request.length,
+ req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ req->trb = NULL;
+}
+
+/**
+ * dwc3_gadget_giveback - call struct usb_request's ->complete callback
+ * @dep: The endpoint to whom the request belongs to
+ * @req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ int status)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ dwc3_gadget_del_and_unmap_request(dep, req, status);
+ dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
+ req, dep->name, req->request.actual,
+ req->request.length, status);
+ req->status = DWC3_REQUEST_STATUS_COMPLETED;
+
+ spin_unlock(&dwc->lock);
+ req->request.complete(&dep->endpoint, &req->request);
+ spin_lock(&dwc->lock);
+}
+
+/**
+ * dwc3_send_gadget_generic_command - issue a generic command for the controller
+ * @dwc: pointer to the controller context
+ * @cmd: the command to be issued
+ * @param: command parameter
+ *
+ * Caller should take care of locking. Issue @cmd with a given @param to @dwc
+ * and wait for its completion.
+ */
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
+{
+ u32 timeout = 500;
+ int status = 0;
+ int ret = 0;
+ u32 reg;
+
+ dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
+ dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
+
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
+ if (!(reg & DWC3_DGCMD_CMDACT)) {
+ dev_dbg(dwc->dev, "%s: Command Complete --> %d\n",
+ __func__,
+ DWC3_DGCMD_STATUS(reg));
+ status = DWC3_DGCMD_STATUS(reg);
+ if (status)
+ ret = -EINVAL;
+ break;
+ }
+
+ udelay(1);
+ } while (--timeout);
+
+ if (!timeout) {
+ ret = -ETIMEDOUT;
+ status = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
+/**
+ * dwc3_send_gadget_ep_cmd - issue an endpoint command
+ * @dep: the endpoint to which the command is going to be issued
+ * @cmd: the command to be issued
+ * @params: parameters to the command
+ *
+ * Caller should handle locking. This function will issue @cmd with given
+ * @params to @dep and wait for its completion.
+ */
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+ struct dwc3_gadget_ep_cmd_params *params)
+{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
+ struct dwc3 *dwc = dep->dwc;
+ u32 timeout = 1000;
+ u32 saved_config = 0;
+ u32 reg;
+
+ int cmd_status = 0;
+ int ret = -EINVAL;
+
+ /*
+ * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
+ * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
+ * endpoint command.
+ *
+ * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
+ * settings. Restore them after the command is completed.
+ *
+ * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
+ */
+ if (dwc->gadget.speed <= USB_SPEED_HIGH) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
+ saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ }
+
+ if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
+ saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ }
+
+ if (saved_config)
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ int needs_wakeup;
+
+ needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
+ dwc->link_state == DWC3_LINK_STATE_U2 ||
+ dwc->link_state == DWC3_LINK_STATE_U3);
+
+ if (unlikely(needs_wakeup)) {
+ ret = __dwc3_gadget_wakeup(dwc);
+ dev_warn(dwc->dev, "wakeup failed --> %d\n", ret);
+ }
+ }
+
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+
+ /*
+ * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're
+ * not relying on XferNotReady, we can make use of a special "No
+ * Response Update Transfer" command where we should clear both CmdAct
+ * and CmdIOC bits.
+ *
+ * With this, we don't need to wait for command completion and can
+ * straight away issue further commands to the endpoint.
+ *
+ * NOTICE: We're making an assumption that control endpoints will never
+ * make use of Update Transfer command. This is a safe assumption
+ * because we can never have more than one request at a time with
+ * Control Endpoints. If anybody changes that assumption, this chunk
+ * needs to be updated accordingly.
+ */
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_UPDATETRANSFER &&
+ !usb_endpoint_xfer_isoc(desc))
+ cmd &= ~(DWC3_DEPCMD_CMDIOC | DWC3_DEPCMD_CMDACT);
+ else
+ cmd |= DWC3_DEPCMD_CMDACT;
+
+ dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
+ do {
+ reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
+ if (!(reg & DWC3_DEPCMD_CMDACT)) {
+ cmd_status = DWC3_DEPCMD_STATUS(reg);
+
+ switch (cmd_status) {
+ case 0:
+ ret = 0;
+ break;
+ case DEPEVT_TRANSFER_NO_RESOURCE:
+ ret = -EINVAL;
+ break;
+ case DEPEVT_TRANSFER_BUS_EXPIRY:
+ /*
+ * SW issues START TRANSFER command to
+ * isochronous ep with future frame interval. If
+ * future interval time has already passed when
+ * core receives the command, it will respond
+ * with an error status of 'Bus Expiry'.
+ *
+ * Instead of always returning -EINVAL, let's
+ * give a hint to the gadget driver that this is
+ * the case by returning -EAGAIN.
+ */
+ ret = -EAGAIN;
+ break;
+ default:
+ dev_warn(dwc->dev, "UNKNOWN cmd status\n");
+ }
+
+ break;
+ }
+ } while (--timeout);
+
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ cmd_status = -ETIMEDOUT;
+ }
+
+ if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_get_transfer_index(dep);
+ }
+
+ if (saved_config) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= saved_config;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ return ret;
+
+}
+
+static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd = DWC3_DEPCMD_CLEARSTALL;
+
+ /*
+ * As of core revision 2.60a the recommended programming model
+ * is to set the ClearPendIN bit when issuing a Clear Stall EP
+ * command for IN endpoints. This is to prevent an issue where
+ * some (non-compliant) hosts may not send ACK TPs for pending
+ * IN transfers due to a mishandled error condition. Synopsys
+ * STAR 9000614252.
+ */
+ if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
+ (dwc->gadget.speed >= USB_SPEED_SUPER))
+ cmd |= DWC3_DEPCMD_CLEARPENDIN;
+
+ memset(&params, 0, sizeof(params));
+
+ return dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+}
+
+static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
+ struct dwc3_trb *trb)
+{
+ u32 offset = (char *) trb - (char *) dep->trb_pool;
+
+ return dep->trb_pool_dma + offset;
+}
+
+static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
+{
+ if (dep->trb_pool)
+ return 0;
+
+ dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) *
+ DWC3_TRB_NUM,
+ &dep->trb_pool_dma);
+ if (!dep->trb_pool) {
+ dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
+ dep->name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void dwc3_free_trb_pool(struct dwc3_ep *dep)
+{
+ dma_free_coherent(dep->trb_pool, 0, sizeof(dma_addr_t));
+
+ dep->trb_pool = NULL;
+ dep->trb_pool_dma = 0;
+}
+
+static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+
+ memset(&params, 0x00, sizeof(params));
+
+ params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
+
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
+ &params);
+}
+
+/**
+ * dwc3_gadget_start_config - configure ep resources
+ * @dep: endpoint that is being enabled
+ *
+ * Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's
+ * completion, it will set Transfer Resource for all available endpoints.
+ *
+ * The assignment of transfer resources cannot perfectly follow the data book
+ * due to the fact that the controller driver does not have all knowledge of the
+ * configuration in advance. It is given this information piecemeal by the
+ * composite gadget framework after every SET_CONFIGURATION and
+ * SET_INTERFACE. Trying to follow the databook programming model in this
+ * scenario can cause errors. For two reasons:
+ *
+ * 1) The databook says to do %DWC3_DEPCMD_DEPSTARTCFG for every
+ * %USB_REQ_SET_CONFIGURATION and %USB_REQ_SET_INTERFACE (8.1.5). This is
+ * incorrect in the scenario of multiple interfaces.
+ *
+ * 2) The databook does not mention doing more %DWC3_DEPCMD_DEPXFERCFG for new
+ * endpoint on alt setting (8.1.6).
+ *
+ * The following simplified method is used instead:
+ *
+ * All hardware endpoints can be assigned a transfer resource and this setting
+ * will stay persistent until either a core reset or hibernation. So whenever we
+ * do a %DWC3_DEPCMD_DEPSTARTCFG(0) we can go ahead and do
+ * %DWC3_DEPCMD_DEPXFERCFG for every hardware endpoint as well. We are
+ * guaranteed that there are as many transfer resources as endpoints.
+ *
+ * This function is called for each endpoint when it is being enabled but is
+ * triggered only when called for EP0-out, which always happens first, and which
+ * should only happen in one of the above conditions.
+ */
+static int dwc3_gadget_start_config(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc;
+ u32 cmd;
+ int i;
+ int ret;
+
+ if (dep->number)
+ return 0;
+
+ memset(&params, 0x00, sizeof(params));
+ cmd = DWC3_DEPCMD_DEPSTARTCFG;
+ dwc = dep->dwc;
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
+ struct dwc3_ep *dep = dwc->eps[i];
+
+ if (!dep)
+ continue;
+
+ ret = dwc3_gadget_set_xfer_resource(dep);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
+{
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ const struct usb_endpoint_descriptor *desc;
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc = dep->dwc;
+
+ comp_desc = dep->endpoint.comp_desc;
+ desc = dep->endpoint.desc;
+
+ memset(&params, 0x00, sizeof(params));
+
+ params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
+ | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
+
+ /* Burst size is only needed in SuperSpeed mode */
+ if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ u32 burst = dep->endpoint.maxburst - 1;
+ params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+ }
+
+ params.param0 |= action;
+ if (action == DWC3_DEPCFG_ACTION_RESTORE)
+ params.param2 |= dep->saved_state;
+
+ if (usb_endpoint_xfer_control(desc))
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
+
+ if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
+ params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
+
+ if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
+ params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+ | DWC3_DEPCFG_STREAM_EVENT_EN;
+ dep->stream_capable = true;
+ }
+
+ if (!usb_endpoint_xfer_control(desc))
+ params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
+
+ /*
+ * We are doing 1:1 mapping for endpoints, meaning
+ * Physical Endpoints 2 maps to Logical Endpoint 2 and
+ * so on. We consider the direction bit as part of the physical
+ * endpoint number. So USB endpoint 0x81 is 0x03.
+ */
+ params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number);
+
+ /*
+ * We must use the lower 16 TX FIFOs even though
+ * HW might have more
+ */
+ if (dep->direction)
+ params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1);
+
+ if (desc->bInterval) {
+ params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1);
+ dep->interval = 1 << (desc->bInterval - 1);
+ }
+
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
+}
+
+/**
+ * __dwc3_gadget_ep_enable - Initializes a HW endpoint
+ * @dep: endpoint to be initialized
+ * @desc: USB Endpoint Descriptor
+ *
+ * Caller should take care of locking
+ */
+static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
+{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
+ struct dwc3 *dwc = dep->dwc;
+
+ u32 reg;
+ int ret;
+
+ dev_dbg(dwc->dev, "Enabling %s\n", dep->name);
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ ret = dwc3_gadget_start_config(dep);
+ if (ret)
+ return ret;
+ }
+
+ ret = dwc3_gadget_set_ep_config(dep, action);
+ if (ret)
+ return ret;
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ struct dwc3_trb *trb_st_hw;
+ struct dwc3_trb *trb_link;
+
+ dep->type = usb_endpoint_type(desc);
+ dep->flags |= DWC3_EP_ENABLED;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg |= DWC3_DALEPENA_EP(dep->number);
+ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+
+ if (usb_endpoint_xfer_control(desc))
+ return 0;
+
+ /* Initialize the TRB ring */
+ dep->trb_dequeue = 0;
+ dep->trb_enqueue = 0;
+ memset(dep->trb_pool, 0,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
+
+ /* Link TRB. The HWO bit is never reset */
+ trb_st_hw = &dep->trb_pool[0];
+
+ trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
+ memset(trb_link, 0, sizeof(*trb_link));
+
+ trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep,
+ trb_st_hw));
+ trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep,
+ trb_st_hw));
+ trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
+ trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
+ }
+
+ /*
+ * Issue StartTransfer here with no-op TRB so we can always rely on No
+ * Response Update Transfer command.
+ */
+ if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
+ usb_endpoint_xfer_int(desc)) {
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_trb *trb;
+ dma_addr_t trb_dma;
+ u32 cmd;
+
+ memset(&params, 0, sizeof(params));
+ trb = &dep->trb_pool[0];
+ trb_dma = dwc3_trb_dma_offset(dep, trb);
+
+ params.param0 = upper_32_bits(trb_dma);
+ params.param1 = lower_32_bits(trb_dma);
+
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt);
+static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+
+ dwc3_stop_active_transfer(dep, true, false);
+
+ /* - giveback all requests to gadget driver */
+ while (!list_empty(&dep->started_list)) {
+ req = next_request(&dep->started_list);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
+
+ while (!list_empty(&dep->pending_list)) {
+ req = next_request(&dep->pending_list);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
+}
+
+/**
+ * __dwc3_gadget_ep_disable - Disables a HW endpoint
+ * @dep: the endpoint to disable
+ *
+ * This function also removes requests which are currently processed ny the
+ * hardware and those which are not yet scheduled.
+ * Caller should take care of locking.
+ */
+static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+
+ dwc3_remove_requests(dwc, dep);
+
+ /* make sure HW endpoint isn't stalled */
+ if (dep->flags & DWC3_EP_STALL)
+ __dwc3_gadget_ep_set_halt(dep, 0, false);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg &= ~DWC3_DALEPENA_EP(dep->number);
+ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+
+ dep->stream_capable = false;
+ dep->type = 0;
+ dep->flags = 0;
+
+ /* Clear out the ep descriptors for non-ep0 */
+ if (dep->number > 1) {
+ dep->endpoint.comp_desc = NULL;
+ dep->endpoint.desc = NULL;
+ }
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EINVAL;
+}
+
+static int dwc3_gadget_ep0_disable(struct usb_ep *ep)
+{
+ return -EINVAL;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct dwc3_ep *dep;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ int ret;
+
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_debug("dwc3: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!desc->wMaxPacketSize) {
+ pr_debug("dwc3: missing wMaxPacketSize\n");
+ return -EINVAL;
+ }
+
+ dep = to_dwc3_ep(ep);
+ dwc = dep->dwc;
+
+ if (dep->flags & DWC3_EP_ENABLED) {
+ WARN(true, "%s is already enabled\n",
+ dep->name);
+ return 0;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_disable(struct usb_ep *ep)
+{
+ struct dwc3_ep *dep;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ int ret;
+
+ if (!ep) {
+ pr_debug("dwc3: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ dep = to_dwc3_ep(ep);
+ dwc = dep->dwc;
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ WARN(true, "%s is already disabled\n",
+ dep->name);
+ return 0;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_disable(dep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep)
+{
+ struct dwc3_request *req;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+
+ req = xzalloc(sizeof(*req));
+ if (!req)
+ return NULL;
+
+ req->direction = dep->direction;
+ req->epnum = dep->number;
+ req->dep = dep;
+ req->status = DWC3_REQUEST_STATUS_UNKNOWN;
+
+ return &req->request;
+}
+
+static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+
+ kfree(req);
+}
+
+/**
+ * dwc3_ep_prev_trb - returns the previous TRB in the ring
+ * @dep: The endpoint with the TRB ring
+ * @index: The index of the current TRB in the ring
+ *
+ * Returns the TRB prior to the one pointed to by the index. If the
+ * index is 0, we will wrap backwards, skip the link TRB, and return
+ * the one just before that.
+ */
+static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
+{
+ u8 tmp = index;
+
+ if (!tmp)
+ tmp = DWC3_TRB_NUM - 1;
+
+ return &dep->trb_pool[tmp - 1];
+}
+
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
+{
+ struct dwc3_trb *tmp;
+ u8 trbs_left;
+
+ /*
+ * If enqueue & dequeue are equal than it is either full or empty.
+ *
+ * One way to know for sure is if the TRB right before us has HWO bit
+ * set or not. If it has, then we're definitely full and can't fit any
+ * more transfers in our ring.
+ */
+ if (dep->trb_enqueue == dep->trb_dequeue) {
+ tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+ if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ return 0;
+
+ return DWC3_TRB_NUM - 1;
+ }
+
+ trbs_left = dep->trb_dequeue - dep->trb_enqueue;
+ trbs_left &= (DWC3_TRB_NUM - 1);
+
+ if (dep->trb_dequeue < dep->trb_enqueue)
+ trbs_left--;
+
+ return trbs_left;
+}
+
+static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
+ dma_addr_t dma, unsigned length,
+ unsigned chain, unsigned node,
+ unsigned stream_id, unsigned short_not_ok,
+ unsigned no_interrupt)
+{
+ struct dwc3 *dwc = dep->dwc;
+ struct usb_gadget *gadget = &dwc->gadget;
+ enum usb_device_speed speed = gadget->speed;
+
+ trb->size = DWC3_TRB_SIZE_LENGTH(length);
+ trb->bpl = lower_32_bits(dma);
+ trb->bph = upper_32_bits(dma);
+
+ switch (usb_endpoint_type(dep->endpoint.desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!node) {
+ trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+
+ /*
+ * USB Specification 2.0 Section 5.9.2 states that: "If
+ * there is only a single transaction in the microframe,
+ * only a DATA0 data packet PID is used. If there are
+ * two transactions per microframe, DATA1 is used for
+ * the first transaction data packet and DATA0 is used
+ * for the second transaction data packet. If there are
+ * three transactions per microframe, DATA2 is used for
+ * the first transaction data packet, DATA1 is used for
+ * the second, and DATA0 is used for the third."
+ *
+ * IOW, we should satisfy the following cases:
+ *
+ * 1) length <= maxpacket
+ * - DATA0
+ *
+ * 2) maxpacket < length <= (2 * maxpacket)
+ * - DATA1, DATA0
+ *
+ * 3) (2 * maxpacket) < length <= (3 * maxpacket)
+ * - DATA2, DATA1, DATA0
+ */
+ if (speed == USB_SPEED_HIGH) {
+ struct usb_ep *ep = &dep->endpoint;
+ unsigned int mult = 2;
+ unsigned int maxp = usb_endpoint_maxp(ep->desc);
+
+ if (length <= (2 * maxp))
+ mult--;
+
+ if (length <= maxp)
+ mult--;
+
+ trb->size |= DWC3_TRB_SIZE_PCM1(mult);
+ }
+ } else {
+ trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+ }
+
+ /* always enable Interrupt on Missed ISOC */
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ break;
+
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ trb->ctrl = DWC3_TRBCTL_NORMAL;
+ break;
+ default:
+ /*
+ * This is only possible with faulty memory because we
+ * checked it already :)
+ */
+ dev_warn(dwc->dev, "Unknown endpoint type %d\n",
+ usb_endpoint_type(dep->endpoint.desc));
+ }
+
+ /*
+ * Enable Continue on Short Packet
+ * when endpoint is not a stream capable
+ */
+ if (usb_endpoint_dir_out(dep->endpoint.desc)) {
+ if (!dep->stream_capable)
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
+
+ if (short_not_ok)
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ }
+
+ if ((!no_interrupt && !chain) ||
+ (dwc3_calc_trbs_left(dep) == 1))
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
+
+ if (chain)
+ trb->ctrl |= DWC3_TRB_CTRL_CHN;
+
+ if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
+ trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
+
+ trb->ctrl |= DWC3_TRB_CTRL_HWO;
+
+ dwc3_ep_inc_enq(dep);
+}
+
+/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+ struct dwc3_request *req,
+ unsigned chain, unsigned node)
+{
+ struct dwc3_trb *trb;
+ unsigned int length;
+ dma_addr_t dma;
+ unsigned stream_id = req->request.stream_id;
+ unsigned short_not_ok = req->request.short_not_ok;
+ unsigned no_interrupt = req->request.no_interrupt;
+
+ length = req->request.length;
+ dma = req->request.dma;
+
+ trb = &dep->trb_pool[dep->trb_enqueue];
+
+ if (!req->trb) {
+ dwc3_gadget_move_started_request(req);
+ req->trb = trb;
+ req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+ }
+
+ req->num_trbs++;
+
+ __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
+ stream_id, short_not_ok, no_interrupt);
+}
+
+static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
+ struct dwc3_request *req)
+{
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
+
+ if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->needs_extra_trb = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, 0);
+
+ /* Now prepare one extra TRB to align transfer size */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
+ false, 1, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else if (req->request.zero && req->request.length &&
+ (IS_ALIGNED(req->request.length, maxp))) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->needs_extra_trb = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, 0);
+
+ /* Now prepare one extra TRB to handle ZLP */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
+ false, 1, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, false, 0);
+ }
+}
+
+/*
+ * dwc3_prepare_trbs - setup TRBs from requests
+ * @dep: endpoint for which requests are being prepared
+ * @starting: true if the endpoint is idle and no requests are queued.
+ *
+ * The function goes through the requests list and sets up TRBs for the
+ * transfers. The function returns once there are no more TRBs available or
+ * it runs out of requests.
+ */
+static void dwc3_prepare_trbs(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req, *n;
+ struct dwc3 *dwc = dep->dwc;
+ dma_addr_t dma_addr;
+
+ BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
+
+ list_for_each_entry_safe(req, n, &dep->pending_list, list) {
+ dma_addr = dma_map_single(dwc->dev, req->request.buf,
+ req->request.length,
+ dep->number ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (dma_mapping_error(dwc->dev, dma_addr))
+ return;
+
+ req->request.dma = dma_addr;
+
+ dwc3_prepare_one_trb_linear(dep, req);
+
+ if (!dwc3_calc_trbs_left(dep))
+ return;
+ }
+}
+
+static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_request *req;
+ int starting;
+ int ret;
+ u32 cmd;
+
+ if (!dwc3_calc_trbs_left(dep))
+ return 0;
+
+ starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
+
+ dwc3_prepare_trbs(dep);
+
+ req = next_request(&dep->started_list);
+ if (!req) {
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+ return 0;
+ }
+
+ memset(&params, 0, sizeof(params));
+
+ if (starting) {
+ params.param0 = upper_32_bits(req->trb_dma);
+ params.param1 = lower_32_bits(req->trb_dma);
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+
+ if (dep->stream_capable)
+ cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id);
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
+ } else {
+ cmd = DWC3_DEPCMD_UPDATETRANSFER |
+ DWC3_DEPCMD_PARAM(dep->resource_index);
+ }
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ if (ret < 0) {
+ /*
+ * FIXME we need to iterate over the list of requests
+ * here and stop, unmap, free and del each of the linked
+ * requests instead of what we do now.
+ */
+ if (req->trb)
+ memset(req->trb, 0, sizeof(struct dwc3_trb));
+ dwc3_gadget_del_and_unmap_request(dep, req, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ return DWC3_DSTS_SOFFN(reg);
+}
+
+/**
+ * dwc3_gadget_start_isoc_quirk - workaround invalid frame number
+ * @dep: isoc endpoint
+ *
+ * This function tests for the correct combination of BIT[15:14] from the 16-bit
+ * microframe number reported by the XferNotReady event for the future frame
+ * number to start the isoc transfer.
+ *
+ * In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed
+ * isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the
+ * XferNotReady event are invalid. The driver uses this number to schedule the
+ * isochronous transfer and passes it to the START TRANSFER command. Because
+ * this number is invalid, the command may fail. If BIT[15:14] matches the
+ * internal 16-bit microframe, the START TRANSFER command will pass and the
+ * transfer will start at the scheduled time, if it is off by 1, the command
+ * will still pass, but the transfer will start 2 seconds in the future. For all
+ * other conditions, the START TRANSFER command will fail with bus-expiry.
+ *
+ * In order to workaround this issue, we can test for the correct combination of
+ * BIT[15:14] by sending START TRANSFER commands with different values of
+ * BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart
+ * (or 2 seconds). 4 seconds into the future will result in a bus-expiry status.
+ * As the result, within the 4 possible combinations for BIT[15:14], there will
+ * be 2 successful and 2 failure START COMMAND status. One of the 2 successful
+ * command status will result in a 2-second delay start. The smaller BIT[15:14]
+ * value is the correct combination.
+ *
+ * Since there are only 4 outcomes and the results are ordered, we can simply
+ * test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to
+ * deduce the smaller successful combination.
+ *
+ * Let test0 = test status for combination 'b00 and test1 = test status for 'b01
+ * of BIT[15:14]. The correct combination is as follow:
+ *
+ * if test0 fails and test1 passes, BIT[15:14] is 'b01
+ * if test0 fails and test1 fails, BIT[15:14] is 'b10
+ * if test0 passes and test1 fails, BIT[15:14] is 'b11
+ * if test0 passes and test1 passes, BIT[15:14] is 'b00
+ *
+ * Synopsys STAR 9001202023: Wrong microframe number for isochronous IN
+ * endpoints.
+ */
+static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
+{
+ int cmd_status = 0;
+ bool test0;
+ bool test1;
+
+ while (dep->combo_num < 2) {
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 test_frame_number;
+ u32 cmd;
+
+ /*
+ * Check if we can start isoc transfer on the next interval or
+ * 4 uframes in the future with BIT[15:14] as dep->combo_num
+ */
+ test_frame_number = dep->frame_number & 0x3fff;
+ test_frame_number |= dep->combo_num << 14;
+ test_frame_number += max_t(u32, 4, dep->interval);
+
+ params.param0 = upper_32_bits(dep->dwc->bounce_addr);
+ params.param1 = lower_32_bits(dep->dwc->bounce_addr);
+
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+ cmd |= DWC3_DEPCMD_PARAM(test_frame_number);
+ cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+
+ /* Redo if some other failure beside bus-expiry is received */
+ if (cmd_status && cmd_status != -EAGAIN) {
+ dep->start_cmd_status = 0;
+ dep->combo_num = 0;
+ return 0;
+ }
+
+ /* Store the first test status */
+ if (dep->combo_num == 0)
+ dep->start_cmd_status = cmd_status;
+
+ dep->combo_num++;
+
+ /*
+ * End the transfer if the START_TRANSFER command is successful
+ * to wait for the next XferNotReady to test the command again
+ */
+ if (cmd_status == 0) {
+ dwc3_stop_active_transfer(dep, true, true);
+ return 0;
+ }
+ }
+
+ /* test0 and test1 are both completed at this point */
+ test0 = (dep->start_cmd_status == 0);
+ test1 = (cmd_status == 0);
+
+ if (!test0 && test1)
+ dep->combo_num = 1;
+ else if (!test0 && !test1)
+ dep->combo_num = 2;
+ else if (test0 && !test1)
+ dep->combo_num = 3;
+ else if (test0 && test1)
+ dep->combo_num = 0;
+
+ dep->frame_number &= 0x3fff;
+ dep->frame_number |= dep->combo_num << 14;
+ dep->frame_number += max_t(u32, 4, dep->interval);
+
+ /* Reinitialize test variables */
+ dep->start_cmd_status = 0;
+ dep->combo_num = 0;
+
+ return __dwc3_gadget_kick_transfer(dep);
+}
+
+static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int ret;
+ int i;
+
+ if (list_empty(&dep->pending_list)) {
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+ return -EAGAIN;
+ }
+
+ if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
+ (dwc->revision <= DWC3_USB31_REVISION_160A ||
+ (dwc->revision == DWC3_USB31_REVISION_170A &&
+ dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
+ dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
+
+ if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
+ return dwc3_gadget_start_isoc_quirk(dep);
+ }
+
+ for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
+ dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
+
+ ret = __dwc3_gadget_kick_transfer(dep);
+ if (ret != -EAGAIN)
+ break;
+ }
+
+ return ret;
+}
+
+static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ if (!dep->endpoint.desc) {
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
+ return -ESHUTDOWN;
+ }
+
+ if (req->dep != dep) {
+ WARN(true, "request %p belongs to '%s'\n",
+ &req->request, req->dep->name);
+ return -EINVAL;
+ }
+
+ if (req->status < DWC3_REQUEST_STATUS_COMPLETED) {
+ WARN(true, "request %p already in flight\n", &req->request);
+ return -EINVAL;
+ }
+
+ req->request.actual = 0;
+ req->request.status = -EINPROGRESS;
+
+ list_add_tail(&req->list, &dep->pending_list);
+ req->status = DWC3_REQUEST_STATUS_QUEUED;
+
+ /*
+ * NOTICE: Isochronous endpoints should NEVER be prestarted. We must
+ * wait for a XferNotReady event so we will know what's the current
+ * (micro-)frame number.
+ *
+ * Without this trick, we are very, very likely gonna get Bus Expiry
+ * errors which will force us issue EndTransfer command.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
+ !(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ return 0;
+
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ return __dwc3_gadget_start_isoc(dep);
+ }
+ }
+ }
+
+ return __dwc3_gadget_kick_transfer(dep);
+}
+
+static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+
+ unsigned long flags;
+
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_queue(dep, req);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep,
+ struct dwc3_request *req)
+{
+ int i;
+
+ /*
+ * If request was already started, this means we had to
+ * stop the transfer. With that we also need to ignore
+ * all TRBs used by the request, however TRBs can only
+ * be modified after completion of END_TRANSFER
+ * command. So what we do here is that we wait for
+ * END_TRANSFER completion and only after that, we jump
+ * over TRBs by clearing HWO and incrementing dequeue
+ * pointer.
+ */
+ for (i = 0; i < req->num_trbs; i++) {
+ struct dwc3_trb *trb;
+
+ trb = req->trb + i;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+
+ req->num_trbs = 0;
+}
+
+static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+
+ list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+ dwc3_gadget_ep_skip_trbs(dep, req);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ }
+}
+
+static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_request *r = NULL;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ list_for_each_entry(r, &dep->pending_list, list) {
+ if (r == req)
+ break;
+ }
+
+ if (r != req) {
+ list_for_each_entry(r, &dep->started_list, list) {
+ if (r == req)
+ break;
+ }
+ if (r == req) {
+ /* wait until it is processed */
+ dwc3_stop_active_transfer(dep, true, true);
+
+ if (!r->trb)
+ goto out0;
+
+ dwc3_gadget_move_cancelled_request(req);
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
+ goto out0;
+ else
+ goto out1;
+ }
+ dev_err(dwc->dev, "request %p was not queued to %s\n",
+ request, ep->name);
+ ret = -EINVAL;
+ goto out0;
+ }
+
+out1:
+ /* giveback the request */
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+
+out0:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc = dep->dwc;
+ int ret;
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
+ return -EINVAL;
+ }
+
+ memset(&params, 0x00, sizeof(params));
+
+ if (value) {
+ struct dwc3_trb *trb;
+ unsigned transfer_in_flight;
+ unsigned started;
+
+ if (dep->number > 1)
+ trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+ else
+ trb = &dwc->ep0_trb[dep->trb_enqueue];
+
+ transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO;
+ started = !list_empty(&dep->started_list);
+
+ if (!protocol && ((dep->direction && transfer_in_flight) ||
+ (!dep->direction && started))) {
+ return -EAGAIN;
+ }
+
+ ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL,
+ &params);
+ if (ret)
+ dev_err(dwc->dev, "failed to set STALL on %s\n",
+ dep->name);
+ else
+ dep->flags |= DWC3_EP_STALL;
+ } else {
+ ret = dwc3_send_clear_stall_ep_cmd(dep);
+ if (ret)
+ dev_err(dwc->dev, "failed to clear STALL on %s\n",
+ dep->name);
+ else
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ }
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ unsigned long flags;
+
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_set_halt(dep, value, false);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dep->flags |= DWC3_EP_WEDGE;
+
+ if (dep->number == 0 || dep->number == 1)
+ ret = __dwc3_gadget_ep0_set_halt(ep, 1);
+ else
+ ret = __dwc3_gadget_ep_set_halt(dep, 1, false);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
+ .enable = dwc3_gadget_ep0_enable,
+ .disable = dwc3_gadget_ep0_disable,
+ .alloc_request = dwc3_gadget_ep_alloc_request,
+ .free_request = dwc3_gadget_ep_free_request,
+ .queue = dwc3_gadget_ep0_queue,
+ .dequeue = dwc3_gadget_ep_dequeue,
+ .set_halt = dwc3_gadget_ep0_set_halt,
+ .set_wedge = dwc3_gadget_ep_set_wedge,
+};
+
+static const struct usb_ep_ops dwc3_gadget_ep_ops = {
+ .enable = dwc3_gadget_ep_enable,
+ .disable = dwc3_gadget_ep_disable,
+ .alloc_request = dwc3_gadget_ep_alloc_request,
+ .free_request = dwc3_gadget_ep_free_request,
+ .queue = dwc3_gadget_ep_queue,
+ .dequeue = dwc3_gadget_ep_dequeue,
+ .set_halt = dwc3_gadget_ep_set_halt,
+ .set_wedge = dwc3_gadget_ep_set_wedge,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_get_frame(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ return __dwc3_gadget_get_frame(dwc);
+}
+
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
+{
+ int retries;
+
+ int ret;
+ u32 reg;
+
+ u8 link_state;
+ u8 speed;
+
+ /*
+ * According to the Databook Remote wakeup request should
+ * be issued only when the device is in early suspend state.
+ *
+ * We can check that via USB Link State bits in DSTS register.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ speed = reg & DWC3_DSTS_CONNECTSPD;
+ if ((speed == DWC3_DSTS_SUPERSPEED) ||
+ (speed == DWC3_DSTS_SUPERSPEED_PLUS))
+ return 0;
+
+ link_state = DWC3_DSTS_USBLNKST(reg);
+
+ switch (link_state) {
+ case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
+ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
+ if (ret < 0) {
+ dev_err(dwc->dev, "failed to put link in Recovery\n");
+ return ret;
+ }
+
+ /* Recent versions do this automatically */
+ if (dwc->revision < DWC3_REVISION_194A) {
+ /* write zeroes to Link Change Request */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
+
+ /* poll until Link State changes to ON */
+ retries = 20000;
+
+ while (retries--) {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ /* in HS, means ON */
+ if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
+ break;
+ }
+
+ if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
+ dev_err(dwc->dev, "failed to send remote wakeup\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dwc3_gadget_wakeup(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_wakeup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
+ int is_selfpowered)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->is_selfpowered = !!is_selfpowered;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
+{
+ u32 reg;
+ u32 timeout = 500;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (is_on) {
+ if (dwc->revision <= DWC3_REVISION_187A) {
+ reg &= ~DWC3_DCTL_TRGTULST_MASK;
+ reg |= DWC3_DCTL_TRGTULST_RX_DET;
+ }
+
+ if (dwc->revision >= DWC3_REVISION_194A)
+ reg &= ~DWC3_DCTL_KEEP_CONNECT;
+ reg |= DWC3_DCTL_RUN_STOP;
+
+ if (dwc->has_hibernation)
+ reg |= DWC3_DCTL_KEEP_CONNECT;
+
+ dwc->pullups_connected = true;
+ } else {
+ reg &= ~DWC3_DCTL_RUN_STOP;
+
+ if (dwc->has_hibernation && !suspend)
+ reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
+ dwc->pullups_connected = false;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg &= DWC3_DSTS_DEVCTRLHLT;
+ } while (--timeout && !(!is_on ^ !reg));
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ dev_dbg(dwc->dev, "gadget %s data soft-%s\n",
+ dwc->gadget_driver
+ ? dwc->gadget_driver->function : "no-function",
+ is_on ? "connect" : "disconnect");
+
+ return 0;
+}
+
+static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret;
+
+ is_on = !!is_on;
+
+ /*
+ * Per databook, when we want to stop the gadget, if a control transfer
+ * is still in process, complete it and get the core into setup phase.
+ */
+ if (!is_on && dwc->ep0state != EP0_SETUP_PHASE)
+ dev_warn(dwc->dev, "not in SETUP phase\n");
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = dwc3_gadget_run_stop(dwc, is_on, false);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* Enable all but Start and End of Frame IRQs */
+ reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
+ DWC3_DEVTEN_EVNTOVERFLOWEN |
+ DWC3_DEVTEN_CMDCMPLTEN |
+ DWC3_DEVTEN_ERRTICERREN |
+ DWC3_DEVTEN_WKUPEVTEN |
+ DWC3_DEVTEN_ULSTCNGEN |
+ DWC3_DEVTEN_CONNECTDONEEN |
+ DWC3_DEVTEN_USBRSTEN |
+ DWC3_DEVTEN_DISCONNEVTEN);
+
+ if (dwc->revision < DWC3_REVISION_250A)
+ reg |= DWC3_DEVTEN_ULSTCNGEN;
+
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+}
+
+static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
+{
+ /* mask all interrupts */
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+}
+
+/**
+ * dwc3_gadget_setup_nump - calculate and initialize NUMP field of %DWC3_DCFG
+ * @dwc: pointer to our context structure
+ *
+ * The following looks like complex but it's actually very simple. In order to
+ * calculate the number of packets we can burst at once on OUT transfers, we're
+ * gonna use RxFIFO size.
+ *
+ * To calculate RxFIFO size we need two numbers:
+ * MDWIDTH = size, in bits, of the internal memory bus
+ * RAM2_DEPTH = depth, in MDWIDTH, of internal RAM2 (where RxFIFO sits)
+ *
+ * Given these two numbers, the formula is simple:
+ *
+ * RxFIFO Size = (RAM2_DEPTH * MDWIDTH / 8) - 24 - 16;
+ *
+ * 24 bytes is for 3x SETUP packets
+ * 16 bytes is a clock domain crossing tolerance
+ *
+ * Given RxFIFO Size, NUMP = RxFIFOSize / 1024;
+ */
+static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
+{
+ u32 ram2_depth;
+ u32 mdwidth;
+ u32 nump;
+ u32 reg;
+
+ ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
+ mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+
+ nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
+ nump = min_t(u32, nump, 16);
+
+ /* update NumP */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_NUMP_MASK;
+ reg |= nump << DWC3_DCFG_NUMP_SHIFT;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static int __dwc3_gadget_start(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret = 0;
+ u32 reg;
+
+ /*
+ * We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP
+ * field instead of letting dwc3 itself calculate that automatically.
+ *
+ * This way, we maximize the chances that we'll be able to get several
+ * bursts of data without going through any sort of endpoint throttling.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ if (dwc3_is_usb31(dwc))
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+
+ dwc3_gadget_setup_nump(dwc);
+
+ /* Start with SuperSpeed Default */
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ goto err0;
+ }
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ goto err1;
+ }
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc->link_state = DWC3_LINK_STATE_SS_DIS;
+ dwc3_ep0_out_start(dwc);
+
+ dwc3_gadget_enable_irq(dwc);
+
+ return 0;
+
+err1:
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+
+err0:
+ return ret;
+}
+
+static int dwc3_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret = 0;
+
+ //dwc3_gadget_wakeup(g);
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->gadget_driver) {
+ dev_err(dwc->dev, "%s is already bound to %s\n",
+ dwc->gadget.name,
+ dwc->gadget_driver->function);
+ ret = -EBUSY;
+ goto err1;
+ }
+
+ dwc->gadget_driver = driver;
+
+ __dwc3_gadget_start(dwc);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+
+err1:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc3_gadget_disable_irq(dwc);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+
+ dwc->gadget_driver = NULL;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static void dwc3_gadget_set_speed(struct dwc3 *dwc,
+ enum usb_device_speed speed)
+{
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+ /*
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (dwc->revision < DWC3_REVISION_220A &&
+ !dwc->dis_metastability_quirk) {
+ reg |= DWC3_DCFG_SUPERSPEED;
+ } else {
+ switch (speed) {
+ case USB_SPEED_LOW:
+ reg |= DWC3_DCFG_LOWSPEED;
+ break;
+ case USB_SPEED_FULL:
+ reg |= DWC3_DCFG_FULLSPEED;
+ break;
+ case USB_SPEED_HIGH:
+ reg |= DWC3_DCFG_HIGHSPEED;
+ break;
+ case USB_SPEED_SUPER:
+ reg |= DWC3_DCFG_SUPERSPEED;
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ if (dwc3_is_usb31(dwc))
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED;
+ break;
+ default:
+ dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+
+ if (dwc->revision & DWC3_REVISION_IS_DWC31)
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED;
+ }
+ }
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
+static void dwc3_gadget_poll(struct usb_gadget *g);
+
+static const struct usb_gadget_ops dwc3_gadget_ops = {
+ .get_frame = dwc3_gadget_get_frame,
+ .wakeup = dwc3_gadget_wakeup,
+ .set_selfpowered = dwc3_gadget_set_selfpowered,
+ .pullup = dwc3_gadget_pullup,
+ .udc_start = dwc3_gadget_start,
+ .udc_stop = dwc3_gadget_stop,
+ .udc_poll = dwc3_gadget_poll,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
+ dep->endpoint.maxburst = 1;
+ dep->endpoint.ops = &dwc3_gadget_ep0_ops;
+ if (!dep->direction)
+ dwc->gadget.ep0 = &dep->endpoint;
+
+ return 0;
+}
+
+static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int mdwidth;
+ int kbytes;
+ int size;
+
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth /= 8;
+
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ else
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+
+ /* FIFO Depth is in MDWDITH bytes. Multiply */
+ size *= mdwidth;
+
+ kbytes = size / 1024;
+ if (kbytes == 0)
+ kbytes = 1;
+
+ /*
+ * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
+ * internal overhead. We don't really know how these are used,
+ * but documentation say it exists.
+ */
+ size -= mdwidth * (kbytes + 1);
+ size /= kbytes;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+
+ return dwc3_alloc_trb_pool(dep);
+}
+
+static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+
+ return dwc3_alloc_trb_pool(dep);
+}
+
+static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_ep *dep;
+ bool direction = epnum & 1;
+ int ret;
+ u8 num = epnum >> 1;
+
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ if (!dep)
+ return -ENOMEM;
+
+ dep->dwc = dwc;
+ dep->number = epnum;
+ dep->direction = direction;
+ dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
+ dwc->eps[epnum] = dep;
+ dep->combo_num = 0;
+ dep->start_cmd_status = 0;
+
+ snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
+ direction ? "in" : "out");
+
+ dep->endpoint.name = dep->name;
+
+ if (!(dep->number > 1)) {
+ dep->endpoint.desc = &dwc3_gadget_ep0_desc;
+ dep->endpoint.comp_desc = NULL;
+ }
+
+ spin_lock_init(&dep->lock);
+
+ if (num == 0)
+ ret = dwc3_gadget_init_control_endpoint(dep);
+ else if (direction)
+ ret = dwc3_gadget_init_in_endpoint(dep);
+ else
+ ret = dwc3_gadget_init_out_endpoint(dep);
+
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&dep->pending_list);
+ INIT_LIST_HEAD(&dep->started_list);
+ INIT_LIST_HEAD(&dep->cancelled_list);
+
+ return 0;
+}
+
+static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
+{
+ u8 epnum;
+
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
+
+ for (epnum = 0; epnum < total; epnum++) {
+ int ret;
+
+ ret = dwc3_gadget_init_endpoint(dwc, epnum);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ u8 epnum;
+
+ for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
+ /*
+ * Physical endpoints 0 and 1 are special; they form the
+ * bi-directional USB endpoint 0.
+ *
+ * For those two physical endpoints, we don't allocate a TRB
+ * pool nor do we add them the endpoints list. Due to that, we
+ * shouldn't do these two operations otherwise we would end up
+ * with all sorts of bugs when removing dwc3.ko.
+ */
+ if (epnum != 0 && epnum != 1) {
+ dwc3_free_trb_pool(dep);
+ list_del(&dep->endpoint.ep_list);
+ }
+
+ kfree(dep);
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
+ struct dwc3_request *req,
+ struct dwc3_trb *trb,
+ const struct dwc3_event_depevt *event,
+ int status, int chain)
+{
+ unsigned int count;
+
+ dwc3_ep_inc_deq(dep);
+
+ req->num_trbs--;
+
+ /*
+ * If we're in the middle of series of chained TRBs and we
+ * receive a short transfer along the way, DWC3 will skip
+ * through all TRBs including the last TRB in the chain (the
+ * where CHN bit is zero. DWC3 will also avoid clearing HWO
+ * bit and SW has to do it manually.
+ *
+ * We're going to do that here to avoid problems of HW trying
+ * to use bogus TRBs for transfers.
+ */
+ if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+
+ /*
+ * For isochronous transfers, the first TRB in a service interval must
+ * have the Isoc-First type. Track and report its interval frame number.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (trb->ctrl & DWC3_TRBCTL_ISOCHRONOUS_FIRST)) {
+ unsigned int frame_number;
+
+ frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl);
+ frame_number &= ~(dep->interval - 1);
+ }
+
+ /*
+ * If we're dealing with unaligned size OUT transfer, we will be left
+ * with one TRB pending in the ring. We need to manually clear HWO bit
+ * from that TRB.
+ */
+
+ if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ return 1;
+ }
+
+ count = trb->size & DWC3_TRB_SIZE_MASK;
+ req->remaining += count;
+
+ if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+ return 1;
+
+ if (event->status & DEPEVT_STATUS_SHORT && !chain)
+ return 1;
+
+ if (event->status & DEPEVT_STATUS_IOC)
+ return 1;
+
+ return 0;
+}
+
+static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
+ struct dwc3_request *req,
+ const struct dwc3_event_depevt *event,
+ int status)
+{
+ struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
+
+ return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb,
+ event, status, false);
+}
+
+static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event,
+ struct dwc3_request *req,
+ int status)
+{
+ int ret;
+
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
+ status);
+
+ if (req->needs_extra_trb) {
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
+ status);
+ req->needs_extra_trb = false;
+ }
+
+ req->request.actual = req->request.length - req->remaining;
+
+ dwc3_gadget_giveback(dep, req, status);
+
+ return ret;
+}
+
+static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event,
+ int status)
+{
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+ int ret;
+
+ ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
+ req, status);
+ if (ret)
+ break;
+ }
+}
+
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ dep->frame_number = event->parameters;
+}
+
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3 *dwc = dep->dwc;
+ unsigned status = 0;
+ bool stop = false;
+
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
+ status = -EXDEV;
+
+ if (list_empty(&dep->started_list))
+ stop = true;
+ }
+
+ dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
+
+ if (stop) {
+ dwc3_stop_active_transfer(dep, true, true);
+ dep->flags = DWC3_EP_ENABLED;
+ }
+
+ /*
+ * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
+ * See dwc3_gadget_linksts_change_interrupt() for 1st half.
+ */
+ if (dwc->revision < DWC3_REVISION_183A) {
+ u32 reg;
+ int i;
+
+ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
+ dep = dwc->eps[i];
+
+ if (!(dep->flags & DWC3_EP_ENABLED))
+ continue;
+
+ if (!list_empty(&dep->started_list))
+ return;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= dwc->u1u2;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ dwc->u1u2 = 0;
+ }
+}
+
+static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+ (void) __dwc3_gadget_start_isoc(dep);
+}
+
+static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep;
+ u8 epnum = event->endpoint_number;
+ u8 cmd;
+
+ dep = dwc->eps[epnum];
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ return;
+
+ /* Handle only EPCMDCMPLT when EP disabled */
+ if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
+ return;
+ }
+
+ if (epnum == 0 || epnum == 1) {
+ dwc3_ep0_interrupt(dwc, event);
+ return;
+ }
+
+ switch (event->endpoint_event) {
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ dwc3_gadget_endpoint_transfer_in_progress(dep, event);
+ break;
+ case DWC3_DEPEVT_XFERNOTREADY:
+ dwc3_gadget_endpoint_transfer_not_ready(dep, event);
+ break;
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+ }
+ break;
+ case DWC3_DEPEVT_STREAMEVT:
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ break;
+ }
+}
+
+static void dwc3_disconnect_gadget(struct dwc3 *dwc)
+{
+ if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->disconnect(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_suspend_gadget(struct dwc3 *dwc)
+{
+ if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->suspend(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_resume_gadget(struct dwc3 *dwc)
+{
+ if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->resume(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_reset_gadget(struct dwc3 *dwc)
+{
+ if (!dwc->gadget_driver)
+ return;
+
+ if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+ spin_unlock(&dwc->lock);
+ usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt)
+{
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ return;
+
+ /*
+ * NOTICE: We are violating what the Databook says about the
+ * EndTransfer command. Ideally we would _always_ wait for the
+ * EndTransfer Command Completion IRQ, but that's causing too
+ * much trouble synchronizing between us and gadget driver.
+ *
+ * We have discussed this with the IP Provider and it was
+ * suggested to giveback all requests here, but give HW some
+ * extra time to synchronize with the interconnect. We're using
+ * an arbitraty 100us delay for that.
+ *
+ * Note also that a similar handling was tested by Synopsys
+ * (thanks a lot Paul) and nothing bad has come out of it.
+ * In short, what we're doing is:
+ *
+ * - Issue EndTransfer WITH CMDIOC bit set
+ * - Wait 100us
+ */
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
+ cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ dep->resource_index = 0;
+
+ if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
+ udelay(100);
+}
+
+static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
+{
+ u32 epnum;
+
+ for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ struct dwc3_ep *dep;
+
+ dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
+
+ if (!(dep->flags & DWC3_EP_STALL))
+ continue;
+
+ dep->flags &= ~DWC3_EP_STALL;
+
+ dwc3_send_clear_stall_ep_cmd(dep);
+ }
+}
+
+static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+{
+ int reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ dwc3_disconnect_gadget(dwc);
+
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->setup_packet_pending = false;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+
+ dwc->connected = false;
+}
+
+static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ dwc->connected = true;
+
+ /*
+ * WORKAROUND: DWC3 revisions <1.88a have an issue which
+ * would cause a missing Disconnect Event if there's a
+ * pending Setup Packet in the FIFO.
+ *
+ * There's no suggested workaround on the official Bug
+ * report, which states that "unless the driver/application
+ * is doing any special handling of a disconnect event,
+ * there is no functional issue".
+ *
+ * Unfortunately, it turns out that we _do_ some special
+ * handling of a disconnect event, namely complete all
+ * pending transfers, notify gadget driver of the
+ * disconnection, and so on.
+ *
+ * Our suggested workaround is to follow the Disconnect
+ * Event steps here, instead, based on a setup_packet_pending
+ * flag. Such flag gets set whenever we have a SETUP_PENDING
+ * status for EP0 TRBs and gets cleared on XferComplete for the
+ * same endpoint.
+ *
+ * Refers to:
+ *
+ * STAR#9000466709: RTL: Device : Disconnect event not
+ * generated if setup packet pending in FIFO
+ */
+ if (dwc->revision < DWC3_REVISION_188A) {
+ if (dwc->setup_packet_pending)
+ dwc3_gadget_disconnect_interrupt(dwc);
+ }
+
+ dwc3_reset_gadget(dwc);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_TSTCTRL_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc->test_mode = false;
+ dwc3_clear_stall_all_ep(dwc);
+
+ /* Reset device address to zero */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_DEVADDR_MASK);
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret;
+ u32 reg;
+ u8 speed;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ speed = reg & DWC3_DSTS_CONNECTSPD;
+ dwc->speed = speed;
+
+ switch (speed) {
+ case DWC3_DSTS_SUPERSPEED_PLUS:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ dwc->gadget.ep0->maxpacket = 512;
+ dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+ break;
+ case DWC3_DCFG_SUPERSPEED:
+ /*
+ * WORKAROUND: DWC3 revisions <1.90a have an issue which
+ * would cause a missing USB3 Reset event.
+ *
+ * In such situations, we should force a USB3 Reset
+ * event by calling our dwc3_gadget_reset_interrupt()
+ * routine.
+ *
+ * Refers to:
+ *
+ * STAR#9000483510: RTL: SS : USB3 reset event may
+ * not be generated always when the link enters poll
+ */
+ if (dwc->revision < DWC3_REVISION_190A)
+ dwc3_gadget_reset_interrupt(dwc);
+
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ dwc->gadget.ep0->maxpacket = 512;
+ dwc->gadget.speed = USB_SPEED_SUPER;
+ break;
+ case DWC3_DCFG_HIGHSPEED:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ dwc->gadget.ep0->maxpacket = 64;
+ dwc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case DWC3_DCFG_FULLSPEED:
+ case DWC3_DCFG_FULLSPEED1:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ dwc->gadget.ep0->maxpacket = 64;
+ dwc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case DWC3_DCFG_LOWSPEED:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
+ dwc->gadget.ep0->maxpacket = 8;
+ dwc->gadget.speed = USB_SPEED_LOW;
+ break;
+ }
+
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+
+ /* Enable USB2 LPM Capability */
+
+ if ((dwc->revision > DWC3_REVISION_194A) &&
+ (speed != DWC3_DSTS_SUPERSPEED) &&
+ (speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+ reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
+
+ /*
+ * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
+ * DCFG.LPMCap is set, core responses with an ACK and the
+ * BESL value in the LPM token is less than or equal to LPM
+ * NYET threshold.
+ */
+ if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum)
+ WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+
+ if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
+ reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ } else {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
+
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ return;
+ }
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ return;
+ }
+
+ /*
+ * Configure PHY via GUSB3PIPECTLn if required.
+ *
+ * Update GTXFIFOSIZn
+ *
+ * In both cases reset values should be sufficient.
+ */
+}
+
+static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
+{
+ /*
+ * TODO take core out of low power mode when that's
+ * implemented.
+ */
+
+ if (dwc->gadget_driver && dwc->gadget_driver->resume)
+ dwc->gadget_driver->resume(&dwc->gadget);
+}
+
+static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+ unsigned int pwropt;
+
+ /*
+ * WORKAROUND: DWC3 < 2.50a have an issue when configured without
+ * Hibernation mode enabled which would show up when device detects
+ * host-initiated U3 exit.
+ *
+ * In that case, device will generate a Link State Change Interrupt
+ * from U3 to RESUME which is only necessary if Hibernation is
+ * configured in.
+ *
+ * There are no functional changes due to such spurious event and we
+ * just need to ignore it.
+ *
+ * Refers to:
+ *
+ * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation
+ * operational mode
+ */
+ pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
+ if ((dwc->revision < DWC3_REVISION_250A) &&
+ (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
+ if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
+ (next == DWC3_LINK_STATE_RESUME)) {
+ dev_dbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+ return;
+ }
+ }
+
+ /*
+ * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
+ * on the link partner, the USB session might do multiple entry/exit
+ * of low power states before a transfer takes place.
+ *
+ * Due to this problem, we might experience lower throughput. The
+ * suggested workaround is to disable DCTL[12:9] bits if we're
+ * transitioning from U1/U2 to U0 and enable those bits again
+ * after a transfer completes and there are no pending transfers
+ * on any of the enabled endpoints.
+ *
+ * This is the first half of that workaround.
+ *
+ * Refers to:
+ *
+ * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
+ * core send LGO_Ux entering U0
+ */
+ if (dwc->revision < DWC3_REVISION_183A) {
+ if (next == DWC3_LINK_STATE_U0) {
+ u32 u1u2;
+ u32 reg;
+
+ switch (dwc->link_state) {
+ case DWC3_LINK_STATE_U1:
+ case DWC3_LINK_STATE_U2:
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ u1u2 = reg & (DWC3_DCTL_INITU2ENA
+ | DWC3_DCTL_ACCEPTU2ENA
+ | DWC3_DCTL_INITU1ENA
+ | DWC3_DCTL_ACCEPTU1ENA);
+
+ if (!dwc->u1u2)
+ dwc->u1u2 = reg & u1u2;
+
+ reg &= ~u1u2;
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+ }
+
+ switch (next) {
+ case DWC3_LINK_STATE_U1:
+ if (dwc->speed == USB_SPEED_SUPER)
+ dwc3_suspend_gadget(dwc);
+ break;
+ case DWC3_LINK_STATE_U2:
+ case DWC3_LINK_STATE_U3:
+ //dwc3_suspend_gadget(dwc);
+ break;
+ case DWC3_LINK_STATE_RESUME:
+ dwc3_resume_gadget(dwc);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ dwc->link_state = next;
+}
+
+static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+
+ if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
+ dwc3_suspend_gadget(dwc);
+
+ dwc->link_state = next;
+}
+
+static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ unsigned int is_ss = evtinfo & (1UL << 4);
+
+ /**
+ * WORKAROUND: DWC3 revison 2.20a with hibernation support
+ * have a known issue which can cause USB CV TD.9.23 to fail
+ * randomly.
+ *
+ * Because of this issue, core could generate bogus hibernation
+ * events which SW needs to ignore.
+ *
+ * Refers to:
+ *
+ * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
+ * Device Fallback from SuperSpeed
+ */
+ if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
+ return;
+
+ /* enter hibernation here */
+}
+
+static void dwc3_gadget_interrupt(struct dwc3 *dwc,
+ const struct dwc3_event_devt *event)
+{
+
+ switch (event->type) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ dwc3_gadget_disconnect_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_RESET:
+ dwc3_gadget_reset_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ dwc3_gadget_conndone_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ dwc3_gadget_wakeup_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_HIBER_REQ:
+ if (!dwc->has_hibernation) {
+ WARN(1 ,"unexpected hibernation event\n");
+ break;
+ }
+ dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
+ break;
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
+ break;
+ case DWC3_DEVICE_EVENT_EOPF:
+ dev_dbg(dwc->dev, "End of Periodic Frame\n");
+ /* It changed to be suspend event for version 2.30a and above */
+ if (dwc->revision >= DWC3_REVISION_230A) {
+ /*
+ * Ignore suspend event until the gadget enters into
+ * USB_STATE_CONFIGURED state.
+ */
+ if (dwc->gadget.state >= USB_STATE_CONFIGURED)
+ dwc3_gadget_suspend_interrupt(dwc,
+ event->event_info);
+ }
+ break;
+ case DWC3_DEVICE_EVENT_SOF:
+ dev_dbg(dwc->dev, "Start of Periodic Frame\n");
+ break;
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ dev_dbg(dwc->dev, "Erratic Error\n");
+ break;
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ dev_dbg(dwc->dev, "Command Complete\n");
+ break;
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ dev_dbg(dwc->dev, "Overflow\n");
+ break;
+ default:
+ dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ }
+}
+
+static void dwc3_process_event_entry(struct dwc3 *dwc,
+ const union dwc3_event *event)
+{
+ if (!event->type.is_devspec)
+ dwc3_endpoint_interrupt(dwc, &event->depevt);
+ else if (event->type.type == DWC3_EVENT_TYPE_DEV)
+ dwc3_gadget_interrupt(dwc, &event->devt);
+}
+
+static void dwc3_gadget_poll(struct usb_gadget * g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_event_buffer *evt = dwc->ev_buf;
+ u32 amount;
+ u32 count;
+ void *buf;
+ int pos = 0;
+
+ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ count &= DWC3_GEVNTCOUNT_MASK;
+ if (!count)
+ return;
+
+ buf = xzalloc(count);
+
+ amount = min(count, evt->length - evt->lpos);
+ memcpy(buf, evt->buf + evt->lpos, amount);
+
+ if (amount < count)
+ memcpy(buf + amount, evt->buf, count - amount);
+
+ evt->lpos = (evt->lpos + count) % evt->length;
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+
+ while (count > 0) {
+ union dwc3_event event;
+
+ event.raw = *(u32 *)(buf + pos);
+
+ dwc3_process_event_entry(dwc, &event);
+
+ count -= 4;
+ pos += 4;
+ }
+
+ free(buf);
+}
+
+/**
+ * dwc3_gadget_init - Initializes gadget related registers
+ * @dwc: pointer to our controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+int dwc3_gadget_init(struct dwc3 *dwc)
+{
+ int ret;
+
+ dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2,
+ &dwc->ep0_trb_addr);
+ if (!dwc->ep0_trb) {
+ dev_err(dwc->dev, "failed to allocate ep0 trb\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dwc->setup_buf = xzalloc(DWC3_EP0_SETUP_SIZE);
+ if (!dwc->setup_buf) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE,
+ &dwc->bounce_addr);
+ if (!dwc->bounce) {
+ dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ dwc->gadget.ops = &dwc3_gadget_ops;
+ dwc->gadget.max_speed = USB_SPEED_SUPER;
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget.name = "dwc3-gadget";
+
+ /*
+ * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
+ * on ep out.
+ */
+ dwc->gadget.quirk_ep_out_aligned_size = true;
+
+ if (dwc->revision < DWC3_REVISION_220A &&
+ !dwc->dis_metastability_quirk)
+ dev_info(dwc->dev, "changing max_speed on rev %08x\n",
+ dwc->revision);
+
+ dwc->gadget.max_speed = dwc->maximum_speed;
+
+ /*
+ * REVISIT: Here we should clear all pending IRQs to be
+ * sure we're starting from a well known location.
+ */
+
+ ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
+ if (ret)
+ goto err4;
+
+ ret = usb_add_gadget_udc((struct device_d *)dwc->dev, &dwc->gadget);
+ if (ret) {
+ dev_err(dwc->dev, "failed to register udc\n");
+ goto err4;
+ }
+
+ dwc3_gadget_set_speed(dwc, dwc->maximum_speed);
+
+ return 0;
+
+err4:
+ dwc3_gadget_free_endpoints(dwc);
+err3:
+ dma_free_coherent(dwc->bounce, 0, DWC3_BOUNCE_SIZE);
+
+err2:
+ kfree(dwc->setup_buf);
+
+err1:
+ dma_free_coherent(dwc->ep0_trb, 0, sizeof(*dwc->ep0_trb) * 2);
+
+ return ret;
+}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
new file mode 100644
index 0000000000..3ce748c0b4
--- /dev/null
+++ b/drivers/usb/dwc3/gadget.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/**
+ * gadget.h - DesignWare USB3 DRD Gadget Header
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported
+ * to uboot.
+ *
+ * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending
+ transfers
+ *
+ */
+
+#ifndef __DRIVERS_USB_DWC3_GADGET_H
+#define __DRIVERS_USB_DWC3_GADGET_H
+
+#include <usb/gadget.h>
+#include <linux/list.h>
+#include "io.h"
+
+struct dwc3;
+#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
+#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+
+/* DEPCFG parameter 1 */
+#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
+#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
+#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
+#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
+#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
+#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_BULK_BASED (1 << 30)
+#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
+
+/* DEPCFG parameter 0 */
+#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
+/* This applies for core versions earlier than 1.94a */
+#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
+/* These apply for core versions 1.94a and later */
+#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
+#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
+
+/* DEPXFERCFG parameter 0 */
+#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
+
+/* -------------------------------------------------------------------------- */
+
+#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
+
+static inline struct dwc3_request *next_request(struct list_head *list)
+{
+ if (list_empty(list))
+ return NULL;
+
+ return list_first_entry(list, struct dwc3_request, list);
+}
+
+/**
+ * dwc3_gadget_move_started_request - move @req to the started_list
+ * @req: the request to be moved
+ *
+ * Caller should take care of locking. This function will move @req from its
+ * current list to the endpoint's started_list.
+ */
+static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
+{
+ struct dwc3_ep *dep = req->dep;
+
+ req->status = DWC3_REQUEST_STATUS_STARTED;
+ list_move_tail(&req->list, &dep->started_list);
+}
+
+/**
+ * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
+ * @req: the request to be moved
+ *
+ * Caller should take care of locking. This function will move @req from its
+ * current list to the endpoint's cancelled_list.
+ */
+static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
+{
+ struct dwc3_ep *dep = req->dep;
+
+ req->status = DWC3_REQUEST_STATUS_CANCELLED;
+ list_move_tail(&req->list, &dep->cancelled_list);
+}
+
+void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ int status);
+
+void dwc3_ep0_interrupt(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event);
+void dwc3_ep0_out_start(struct dwc3 *dwc);
+int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
+int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request);
+int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
+void dwc3_gadget_handle_interrupt(struct dwc3 *dwc);
+
+/**
+ * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
+ * @dep: dwc3 endpoint
+ *
+ * Caller should take care of locking. Returns the transfer resource
+ * index for a given endpoint.
+ */
+static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
+{
+ u32 res_id;
+
+ res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
+ dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
+}
+
+#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index ca1bfc1b4e..9d6a262038 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -19,7 +19,6 @@ config USB_GADGET_DRIVER_AT91
bool
prompt "at91 gadget driver"
depends on ARCH_AT91
- depends on !ARCH_SAMA5D4
default y
select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index efbc574c23..411464690d 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -31,9 +31,7 @@
#include <asm/byteorder.h>
#include <mach/hardware.h>
-#if defined CONFIG_ARCH_AT91SAM9261 || defined CONFIG_ARCH_AT91SAM9G10
#include <mach/at91sam9261.h>
-#endif
#include <mach/board.h>
#include <mach/cpu.h>
#include <mach/at91sam9261_matrix.h>
@@ -693,12 +691,10 @@ static void pullup(struct at91_udc *udc, int is_on)
txvc |= AT91_UDP_TXVC_PUON;
at91_udp_write(udc, AT91_UDP_TXVC, txvc);
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
-#if defined CONFIG_ARCH_AT91SAM9261 || defined CONFIG_ARCH_AT91SAM9G10
u32 usbpucr;
usbpucr = readl(AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR);
usbpucr |= AT91SAM9261_MATRIX_USBPUCR_PUON;
writel(usbpucr, AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR);
-#endif
}
} else {
stop_activity(udc);
@@ -712,12 +708,10 @@ static void pullup(struct at91_udc *udc, int is_on)
txvc &= ~AT91_UDP_TXVC_PUON;
at91_udp_write(udc, AT91_UDP_TXVC, txvc);
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
-#if defined CONFIG_ARCH_AT91SAM9261 || defined CONFIG_ARCH_AT91SAM9G10
u32 usbpucr;
usbpucr = readl(AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR);
usbpucr &= ~AT91SAM9261_MATRIX_USBPUCR_PUON;
writel(usbpucr, AT91SAM9261_BASE_MATRIX + AT91SAM9261_MATRIX_USBPUCR);
-#endif
}
clk_off(udc);
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 1cfc49d1c5..b66aa6be97 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1194,10 +1194,45 @@ EXPORT_SYMBOL_GPL(usb_string_ids_n);
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
{
+ struct usb_composite_dev *cdev;
+
if (req->status || req->actual != req->length)
DBG((struct usb_composite_dev *) ep->driver_data,
"setup complete --> %d, %d/%d\n",
req->status, req->actual, req->length);
+
+ /*
+ * REVIST The same ep0 requests are shared with function drivers
+ * so they don't have to maintain the same ->complete() stubs.
+ *
+ * Because of that, we need to check for the validity of ->context
+ * here, even though we know we've set it to something useful.
+ */
+ if (!req->context)
+ return;
+
+ cdev = req->context;
+
+ if (cdev->req == req)
+ cdev->setup_pending = false;
+ else
+ WARN(1, "unknown request %p\n", req);
+}
+
+static int composite_ep0_queue(struct usb_composite_dev *cdev,
+ struct usb_request *req)
+{
+ int ret;
+
+ ret = usb_ep_queue(cdev->gadget->ep0, req);
+ if (ret == 0) {
+ if (cdev->req == req)
+ cdev->setup_pending = true;
+ else
+ WARN(1, "unknown request %p\n", req);
+ }
+
+ return ret;
}
/*
@@ -1226,6 +1261,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
* when we delegate to it.
*/
req->zero = 0;
+ req->context = cdev;
req->complete = composite_setup_complete;
req->length = 0;
gadget->ep0->driver_data = cdev;
@@ -1469,7 +1505,7 @@ unknown:
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
req->zero = value < w_length;
- value = usb_ep_queue(gadget->ep0, req);
+ value = composite_ep0_queue(cdev, req);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
@@ -1621,7 +1657,8 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
kfree(uc);
}
if (cdev->req) {
- usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
+ if (cdev->setup_pending)
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
kfree(cdev->req->buf);
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
}
@@ -1753,7 +1790,7 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);
req->length = 0;
- value = usb_ep_queue(cdev->gadget->ep0, req);
+ value = composite_ep0_queue(cdev, req);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index f0f576d708..ced568921b 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -279,25 +279,6 @@ struct usb_ep *usb_ep_autoconfig_ss(
ep_comp))
goto found_ep;
}
-
-#ifdef CONFIG_BLACKFIN
- } else if (gadget_is_musbhdrc(gadget)) {
- if ((USB_ENDPOINT_XFER_BULK == type) ||
- (USB_ENDPOINT_XFER_ISOC == type)) {
- if (USB_DIR_IN & desc->bEndpointAddress)
- ep = find_ep (gadget, "ep5in");
- else
- ep = find_ep (gadget, "ep6out");
- } else if (USB_ENDPOINT_XFER_INT == type) {
- if (USB_DIR_IN & desc->bEndpointAddress)
- ep = find_ep(gadget, "ep1in");
- else
- ep = find_ep(gadget, "ep2out");
- } else
- ep = NULL;
- if (ep && ep_matches(gadget, ep, desc, ep_comp))
- goto found_ep;
-#endif
}
/* Second, look at endpoints until an unclaimed one looks usable */
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index e357456098..096f05ed48 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -108,6 +108,21 @@ void usb_gadget_set_state(struct usb_gadget *gadget,
}
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
+/**
+ * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
+ * @gadget: The gadget which bus reset occurs
+ * @driver: The gadget driver we want to notify
+ *
+ * If the udc driver has bus reset handler, it needs to call this when the bus
+ * reset occurs, it notifies the gadget driver that the bus reset occurs as
+ * well as updates gadget state.
+ */
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
/* ------------------------------------------------------------------------- */
/**
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 84a05c4f96..4c11e6580c 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -121,7 +121,6 @@ MODULE_ALIAS("platform:" MUSB_DRIVER_NAME);
/*-------------------------------------------------------------------------*/
-#ifndef CONFIG_BLACKFIN
static int musb_ulpi_read(struct usb_phy *phy, u32 offset)
{
void __iomem *addr = phy->io_priv;
@@ -195,10 +194,6 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data)
out:
return ret;
}
-#else
-#define musb_ulpi_read NULL
-#define musb_ulpi_write NULL
-#endif
struct usb_phy_io_ops musb_ulpi_access = {
.read = musb_ulpi_read,
@@ -207,7 +202,7 @@ struct usb_phy_io_ops musb_ulpi_access = {
/*-------------------------------------------------------------------------*/
-#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN)
+#if !defined(CONFIG_USB_MUSB_TUSB6010)
/*
* Load an endpoint's FIFO
@@ -824,11 +819,9 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
} else {
musb->is_multipoint = 0;
type = "";
-#ifndef CONFIG_USB_OTG_BLACKLIST_HUB
printk(KERN_ERR
"%s: kernel must blacklist external hubs\n",
musb_driver_name);
-#endif
}
/* log release info */
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index a21ddd5df2..d9402fcc4a 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -122,7 +122,7 @@ enum musb_g_ep0_state {
*/
#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_SOC_OMAP2430) \
- || defined(CONFIG_SOC_OMAP3430) || defined(CONFIG_BLACKFIN) \
+ || defined(CONFIG_SOC_OMAP3430) \
|| defined(CONFIG_ARCH_OMAP4)
/* REVISIT indexed access seemed to
* misbehave (on DaVinci) for at least peripheral IN ...
@@ -437,34 +437,6 @@ static inline struct musb *gadget_to_musb(struct usb_gadget *g)
return container_of(g, struct musb, g);
}
-#ifdef CONFIG_BLACKFIN
-static inline int musb_read_fifosize(struct musb *musb,
- struct musb_hw_ep *hw_ep, u8 epnum)
-{
- musb->nr_endpoints++;
- musb->epmask |= (1 << epnum);
-
- if (epnum < 5) {
- hw_ep->max_packet_sz_tx = 128;
- hw_ep->max_packet_sz_rx = 128;
- } else {
- hw_ep->max_packet_sz_tx = 1024;
- hw_ep->max_packet_sz_rx = 1024;
- }
- hw_ep->is_shared_fifo = false;
-
- return 0;
-}
-
-static inline void musb_configure_ep0(struct musb *musb)
-{
- musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
- musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
- musb->endpoints[0].is_shared_fifo = true;
-}
-
-#else
-
static inline int musb_read_fifosize(struct musb *musb,
struct musb_hw_ep *hw_ep, u8 epnum)
{
@@ -501,8 +473,6 @@ static inline void musb_configure_ep0(struct musb *musb)
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].is_shared_fifo = true;
}
-#endif /* CONFIG_BLACKFIN */
-
/***************************** Glue it together *****************************/
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 1345a4ff04..97a03cbcf4 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -80,17 +80,6 @@ struct musb_hw_ep;
#define tusb_dma_omap() 0
#endif
-/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1
- * Only allow DMA mode 1 to be used when the USB will actually generate the
- * interrupts we expect.
- */
-#ifdef CONFIG_BLACKFIN
-# undef USE_MODE1
-# if !ANOMALY_05000456
-# define USE_MODE1
-# endif
-#endif
-
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h
index a4be339355..ec474477a1 100644
--- a/drivers/usb/musb/musb_io.h
+++ b/drivers/usb/musb/musb_io.h
@@ -37,8 +37,6 @@
#include <io.h>
-#ifndef CONFIG_BLACKFIN
-
/* NOTE: these offsets are all in bytes */
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
@@ -97,26 +95,4 @@ static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
#endif /* CONFIG_USB_MUSB_TUSB6010 */
-#else
-
-static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
- { return (u8) (bfin_read16(addr + offset)); }
-
-static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
- { return bfin_read16(addr + offset); }
-
-static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
- { return (u32) (bfin_read16(addr + offset)); }
-
-static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
- { bfin_write16(addr + offset, (u16) data); }
-
-static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
- { bfin_write16(addr + offset, data); }
-
-static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
- { bfin_write16(addr + offset, (u16) data); }
-
-#endif /* CONFIG_BLACKFIN */
-
#endif
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index b9bcda5e39..2cb749140b 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -220,8 +220,6 @@
#define MUSB_HUBADDR_MULTI_TT 0x80
-#ifndef CONFIG_BLACKFIN
-
/*
* Common USB registers
*/
@@ -460,193 +458,4 @@ static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT));
}
-#else /* CONFIG_BLACKFIN */
-
-#define USB_BASE USB_FADDR
-#define USB_OFFSET(reg) (reg - USB_BASE)
-
-/*
- * Common USB registers
- */
-#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */
-#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */
-#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */
-#define MUSB_INTRRX USB_OFFSET(USB_INTRRX)
-#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE)
-#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE)
-#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */
-#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */
-#define MUSB_FRAME USB_OFFSET(USB_FRAME)
-#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */
-#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */
-
-/* Get offset for a given FIFO from musb->mregs */
-#define MUSB_FIFO_OFFSET(epnum) \
- (USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8))
-
-/*
- * Additional Control Registers
- */
-
-#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */
-
-#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */
-#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */
-#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */
-#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */
-#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */
-
-/* Offsets to endpoint registers */
-#define MUSB_TXMAXP 0x00
-#define MUSB_TXCSR 0x04
-#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
-#define MUSB_RXMAXP 0x08
-#define MUSB_RXCSR 0x0C
-#define MUSB_RXCOUNT 0x10
-#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
-#define MUSB_TXTYPE 0x14
-#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
-#define MUSB_TXINTERVAL 0x18
-#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
-#define MUSB_RXTYPE 0x1C
-#define MUSB_RXINTERVAL 0x20
-#define MUSB_TXCOUNT 0x28
-
-/* Offsets to endpoint registers in indexed model (using INDEX register) */
-#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
- (0x40 + (_offset))
-
-/* Offsets to endpoint registers in flat models */
-#define MUSB_FLAT_OFFSET(_epnum, _offset) \
- (USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset))
-
-/* Not implemented - HW has separate Tx/Rx FIFO */
-#define MUSB_TXCSR_MODE 0x0000
-
-static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
-{
-}
-
-static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
-{
-}
-
-static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
-{
-}
-
-static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
-{
-}
-
-static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val)
-{
-}
-
-static inline u8 musb_read_txfifosz(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u16 musb_read_txfifoadd(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u8 musb_read_rxfifosz(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u16 musb_read_rxfifoadd(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u8 musb_read_configdata(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u16 musb_read_hwvers(void __iomem *mbase)
-{
- /*
- * This register is invisible on Blackfin, actually the MUSB
- * RTL version of Blackfin is 1.9, so just harcode its value.
- */
- return MUSB_HWVERS_1900;
-}
-
-static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
-{
- return NULL;
-}
-
-static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
- u8 qh_addr_req)
-{
-}
-
-static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
- u8 qh_h_addr_reg)
-{
-}
-
-static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
- u8 qh_h_port_reg)
-{
-}
-
-static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
- u8 qh_addr_reg)
-{
-}
-
-static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
- u8 qh_addr_reg)
-{
-}
-
-static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
- u8 qh_h_port_reg)
-{
-}
-
-static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-#endif /* CONFIG_BLACKFIN */
-
#endif /* __MUSB_REGS_H__ */
diff --git a/drivers/video/imx.c b/drivers/video/imx.c
index 947f8d5349..d15c2d88fb 100644
--- a/drivers/video/imx.c
+++ b/drivers/video/imx.c
@@ -152,8 +152,6 @@ struct imxfb_info {
cmap_static:1,
unused:30;
- struct imx_fb_videomode *mode;
-
struct fb_info info;
struct device_d *dev;
@@ -293,14 +291,6 @@ static int imxfb_activate_var(struct fb_info *info)
unsigned long long tmp;
struct imxfb_info *fbi = info->priv;
u32 pcr;
- int i;
-
- for (i = 0; i < info->modes.num_modes; i++) {
- if (!strcmp(fbi->mode[i].mode.name, mode->name)) {
- fbi->pcr = fbi->mode[i].pcr;
- break;
- }
- }
/* physical screen start address */
writel(VPW_VPW(mode->xres * info->bits_per_pixel / 8 / 4),
@@ -556,7 +546,7 @@ static int imxfb_probe(struct device_d *dev)
mode_list = xzalloc(sizeof(*mode_list) * pdata->num_modes);
for (i = 0; i < pdata->num_modes; i++)
- mode_list[i] = pdata->mode[i].mode;
+ mode_list[i] = pdata->mode[i];
fbi = xzalloc(sizeof(*fbi));
info = &fbi->info;
@@ -573,13 +563,12 @@ static int imxfb_probe(struct device_d *dev)
if (IS_ERR(fbi->ipg_clk))
return PTR_ERR(fbi->ipg_clk);
- fbi->mode = pdata->mode;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
fbi->regs = IOMEM(iores->start);
- fbi->pcr = pdata->mode->pcr;
+ fbi->pcr = pdata->pcr;
fbi->pwmr = pdata->pwmr;
fbi->lscr1 = pdata->lscr1;
fbi->dmacr = pdata->dmacr;
@@ -588,10 +577,10 @@ static int imxfb_probe(struct device_d *dev)
info->priv = fbi;
info->modes.modes = mode_list;
info->modes.num_modes = pdata->num_modes;
- info->mode = &pdata->mode->mode;
- info->xres = pdata->mode->mode.xres;
- info->yres = pdata->mode->mode.yres;
- info->bits_per_pixel = pdata->mode->bpp;
+ info->mode = pdata->mode;
+ info->xres = pdata->mode->xres;
+ info->yres = pdata->mode->yres;
+ info->bits_per_pixel = pdata->bpp;
info->fbops = &imxfb_ops;
dev_info(dev, "i.MX Framebuffer driver\n");