aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r--drivers/usb/dwc2/Kconfig61
-rw-r--r--drivers/usb/dwc2/Makefile37
-rw-r--r--drivers/usb/dwc2/core.c68
-rw-r--r--drivers/usb/dwc2/core.h182
-rw-r--r--drivers/usb/dwc2/gadget.c3673
-rw-r--r--drivers/usb/dwc2/hw.h12
-rw-r--r--drivers/usb/dwc2/platform.c6
7 files changed, 4003 insertions, 36 deletions
diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig
index be947d67384..f93807b3631 100644
--- a/drivers/usb/dwc2/Kconfig
+++ b/drivers/usb/dwc2/Kconfig
@@ -1,25 +1,58 @@
config USB_DWC2
- tristate "DesignWare USB2 DRD Core Support"
+ bool "DesignWare USB2 DRD Core Support"
depends on USB
help
- Say Y or M here if your system has a Dual Role HighSpeed
- USB controller based on the DesignWare HSOTG IP Core.
+ Say Y here if your system has a Dual Role Hi-Speed USB
+ controller based on the DesignWare HSOTG IP Core.
- If you choose to build this driver as dynamically linked
- modules, the core module will be called dwc2.ko, the
- PCI bus interface module (if you have a PCI bus system)
- will be called dwc2_pci.ko and the platform interface module
- (for controllers directly connected to the CPU) will be called
- dwc2_platform.ko.
+ For host mode, if you choose to build the driver as dynamically
+ linked modules, the core module will be called dwc2.ko, the PCI
+ bus interface module (if you have a PCI bus system) will be
+ called dwc2_pci.ko, and the platform interface module (for
+ controllers directly connected to the CPU) will be called
+ dwc2_platform.ko. For gadget mode, there will be a single
+ module called dwc2_gadget.ko.
- NOTE: This driver at present only implements the Host mode
- of the controller. The existing s3c-hsotg driver supports
- Peripheral mode, but only for the Samsung S3C platforms.
- There are plans to merge the s3c-hsotg driver with this
- driver in the near future to create a dual-role driver.
+ NOTE: The s3c-hsotg driver is now renamed to dwc2_gadget. The
+ host and gadget drivers are still currently separate drivers.
+ There are plans to merge the dwc2_gadget driver with the dwc2
+ host driver in the near future to create a dual-role driver.
if USB_DWC2
+config USB_DWC2_HOST
+ tristate "Host only mode"
+ depends on USB
+ help
+ The Designware USB2.0 high-speed host controller
+ integrated into many SoCs.
+
+config USB_DWC2_PLATFORM
+ bool "DWC2 Platform"
+ depends on USB_DWC2_HOST
+ default USB_DWC2_HOST
+ help
+ The Designware USB2.0 platform interface module for
+ controllers directly connected to the CPU. This is only
+ used for host mode.
+
+config USB_DWC2_PCI
+ bool "DWC2 PCI"
+ depends on USB_DWC2_HOST && PCI
+ default USB_DWC2_HOST
+ help
+ The Designware USB2.0 PCI interface module for controllers
+ connected to a PCI bus. This is only used for host mode.
+
+comment "Gadget mode requires USB Gadget support to be enabled"
+
+config USB_DWC2_PERIPHERAL
+ tristate "Gadget only mode"
+ depends on USB_GADGET
+ help
+ The Designware USB2.0 high-speed gadget controller
+ integrated into many SoCs.
+
config USB_DWC2_DEBUG
bool "Enable Debugging Messages"
help
diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile
index 11529d3439b..b73d2a52797 100644
--- a/drivers/usb/dwc2/Makefile
+++ b/drivers/usb/dwc2/Makefile
@@ -1,25 +1,28 @@
ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
-obj-$(CONFIG_USB_DWC2) += dwc2.o
-
-dwc2-y += core.o core_intr.o
-
-# NOTE: This driver at present only implements the Host mode
-# of the controller. The existing s3c-hsotg driver supports
-# Peripheral mode, but only for the Samsung S3C platforms.
-# There are plans to merge the s3c-hsotg driver with this
-# driver in the near future to create a dual-role driver. Once
-# that is done, Host mode will become an optional feature that
-# is selected with a config option.
-
+obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o
+dwc2-y := core.o core_intr.o
dwc2-y += hcd.o hcd_intr.o
dwc2-y += hcd_queue.o hcd_ddma.o
-ifneq ($(CONFIG_PCI),)
- obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
+# NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to
+# this location and renamed gadget.c. When building for dynamically linked
+# modules, dwc2_gadget.ko will get built for peripheral mode. For host mode,
+# the core module will be dwc2.ko, the PCI bus interface module will called
+# dwc2_pci.ko and the platform interface module will be called dwc2_platform.ko.
+# At present the host and gadget driver will be separate drivers, but there
+# are plans in the near future to create a dual-role driver.
+
+ifneq ($(CONFIG_USB_DWC2_PCI),)
+ obj-$(CONFIG_USB_DWC2_HOST) += dwc2_pci.o
+ dwc2_pci-y := pci.o
+endif
+
+ifneq ($(CONFIG_USB_DWC2_PLATFORM),)
+ obj-$(CONFIG_USB_DWC2_HOST) += dwc2_platform.o
+ dwc2_platform-y := platform.o
endif
-obj-$(CONFIG_USB_DWC2) += dwc2_platform.o
-dwc2_pci-y += pci.o
-dwc2_platform-y += platform.o
+obj-$(CONFIG_USB_DWC2_PERIPHERAL) += dwc2_gadget.o
+dwc2_gadget-y := gadget.o
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 1d129884cc3..27d2c9b8a03 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -507,6 +507,72 @@ void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
writel(intmsk, hsotg->regs + GINTMSK);
}
+/*
+ * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size
+ * For system that have a total fifo depth that is smaller than the default
+ * RX + TX fifo size.
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *params = hsotg->core_params;
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size;
+
+ total_fifo_size = hw->total_fifo_size;
+ rxfsiz = params->host_rx_fifo_size;
+ nptxfsiz = params->host_nperio_tx_fifo_size;
+ ptxfsiz = params->host_perio_tx_fifo_size;
+
+ /*
+ * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth
+ * allocation with support for high bandwidth endpoints. Synopsys
+ * defines MPS(Max Packet size) for a periodic EP=1024, and for
+ * non-periodic as 512.
+ */
+ if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) {
+ /*
+ * For Buffer DMA mode/Scatter Gather DMA mode
+ * 2 * ((Largest Packet size / 4) + 1 + 1) + n
+ * with n = number of host channel.
+ * 2 * ((1024/4) + 2) = 516
+ */
+ rxfsiz = 516 + hw->host_channels;
+
+ /*
+ * min non-periodic tx fifo depth
+ * 2 * (largest non-periodic USB packet used / 4)
+ * 2 * (512/4) = 256
+ */
+ nptxfsiz = 256;
+
+ /*
+ * min periodic tx fifo depth
+ * (largest packet size*MC)/4
+ * (1024 * 3)/4 = 768
+ */
+ ptxfsiz = 768;
+
+ params->host_rx_fifo_size = rxfsiz;
+ params->host_nperio_tx_fifo_size = nptxfsiz;
+ params->host_perio_tx_fifo_size = ptxfsiz;
+ }
+
+ /*
+ * If the summation of RX, NPTX and PTX fifo sizes is still
+ * bigger than the total_fifo_size, then we have a problem.
+ *
+ * We won't be able to allocate as many endpoints. Right now,
+ * we're just printing an error message, but ideally this FIFO
+ * allocation algorithm would be improved in the future.
+ *
+ * FIXME improve this FIFO allocation algorithm.
+ */
+ if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)))
+ dev_err(hsotg->dev, "invalid fifo sizes\n");
+}
+
static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *params = hsotg->core_params;
@@ -515,6 +581,8 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
if (!params->enable_dynamic_fifo)
return;
+ dwc2_calculate_dynamic_fifo(hsotg);
+
/* Rx FIFO */
grxfsiz = readl(hsotg->regs + GRXFSIZ);
dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 648519c024b..1efd10cc962 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -37,6 +37,10 @@
#ifndef __DWC2_CORE_H__
#define __DWC2_CORE_H__
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
#include <linux/usb/phy.h>
#include "hw.h"
@@ -54,6 +58,184 @@ static inline void do_write(u32 value, void *addr)
/* Maximum number of Endpoints/HostChannels */
#define MAX_EPS_CHANNELS 16
+/* s3c-hsotg declarations */
+static const char * const s3c_hsotg_supply_names[] = {
+ "vusb_d", /* digital USB supply, 1.2V */
+ "vusb_a", /* analog USB supply, 1.1V */
+};
+
+/*
+ * EP0_MPS_LIMIT
+ *
+ * Unfortunately there seems to be a limit of the amount of data that can
+ * be transferred by IN transactions on EP0. This is either 127 bytes or 3
+ * packets (which practically means 1 packet and 63 bytes of data) when the
+ * MPS is set to 64.
+ *
+ * This means if we are wanting to move >127 bytes of data, we need to
+ * split the transactions up, but just doing one packet at a time does
+ * not work (this may be an implicit DATA0 PID on first packet of the
+ * transaction) and doing 2 packets is outside the controller's limits.
+ *
+ * If we try to lower the MPS size for EP0, then no transfers work properly
+ * for EP0, and the system will fail basic enumeration. As no cause for this
+ * has currently been found, we cannot support any large IN transfers for
+ * EP0.
+ */
+#define EP0_MPS_LIMIT 64
+
+struct s3c_hsotg;
+struct s3c_hsotg_req;
+
+/**
+ * struct s3c_hsotg_ep - driver endpoint definition.
+ * @ep: The gadget layer representation of the endpoint.
+ * @name: The driver generated name for the endpoint.
+ * @queue: Queue of requests for this endpoint.
+ * @parent: Reference back to the parent device structure.
+ * @req: The current request that the endpoint is processing. This is
+ * used to indicate an request has been loaded onto the endpoint
+ * and has yet to be completed (maybe due to data move, or simply
+ * awaiting an ack from the core all the data has been completed).
+ * @debugfs: File entry for debugfs file for this endpoint.
+ * @lock: State lock to protect contents of endpoint.
+ * @dir_in: Set to true if this endpoint is of the IN direction, which
+ * means that it is sending data to the Host.
+ * @index: The index for the endpoint registers.
+ * @mc: Multi Count - number of transactions per microframe
+ * @interval - Interval for periodic endpoints
+ * @name: The name array passed to the USB core.
+ * @halted: Set if the endpoint has been halted.
+ * @periodic: Set if this is a periodic ep, such as Interrupt
+ * @isochronous: Set if this is a isochronous ep
+ * @sent_zlp: Set if we've sent a zero-length packet.
+ * @total_data: The total number of data bytes done.
+ * @fifo_size: The size of the FIFO (for periodic IN endpoints)
+ * @fifo_load: The amount of data loaded into the FIFO (periodic IN)
+ * @last_load: The offset of data for the last start of request.
+ * @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
+ *
+ * This is the driver's state for each registered enpoint, allowing it
+ * to keep track of transactions that need doing. Each endpoint has a
+ * lock to protect the state, to try and avoid using an overall lock
+ * for the host controller as much as possible.
+ *
+ * For periodic IN endpoints, we have fifo_size and fifo_load to try
+ * and keep track of the amount of data in the periodic FIFO for each
+ * of these as we don't have a status register that tells us how much
+ * is in each of them. (note, this may actually be useless information
+ * as in shared-fifo mode periodic in acts like a single-frame packet
+ * buffer than a fifo)
+ */
+struct s3c_hsotg_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct s3c_hsotg *parent;
+ struct s3c_hsotg_req *req;
+ struct dentry *debugfs;
+
+ unsigned long total_data;
+ unsigned int size_loaded;
+ unsigned int last_load;
+ unsigned int fifo_load;
+ unsigned short fifo_size;
+
+ unsigned char dir_in;
+ unsigned char index;
+ unsigned char mc;
+ unsigned char interval;
+
+ unsigned int halted:1;
+ unsigned int periodic:1;
+ unsigned int isochronous:1;
+ unsigned int sent_zlp:1;
+
+ char name[10];
+};
+
+/**
+ * struct s3c_hsotg - driver state.
+ * @dev: The parent device supplied to the probe function
+ * @driver: USB gadget driver
+ * @phy: The otg phy transceiver structure for phy control.
+ * @uphy: The otg phy transceiver structure for old USB phy control.
+ * @plat: The platform specific configuration data. This can be removed once
+ * all SoCs support usb transceiver.
+ * @regs: The memory area mapped for accessing registers.
+ * @irq: The IRQ number we are using
+ * @supplies: Definition of USB power supplies
+ * @phyif: PHY interface width
+ * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
+ * @num_of_eps: Number of available EPs (excluding EP0)
+ * @debug_root: root directrory for debugfs.
+ * @debug_file: main status file for debugfs.
+ * @debug_fifo: FIFO status file for debugfs.
+ * @ep0_reply: Request used for ep0 reply.
+ * @ep0_buff: Buffer for EP0 reply data, if needed.
+ * @ctrl_buff: Buffer for EP0 control requests.
+ * @ctrl_req: Request for EP0 control packets.
+ * @setup: NAK management for EP0 SETUP
+ * @last_rst: Time of last reset
+ * @eps: The endpoints being supplied to the gadget framework
+ */
+struct s3c_hsotg {
+ struct device *dev;
+ struct usb_gadget_driver *driver;
+ struct phy *phy;
+ struct usb_phy *uphy;
+ struct s3c_hsotg_plat *plat;
+
+ spinlock_t lock;
+
+ void __iomem *regs;
+ int irq;
+ struct clk *clk;
+
+ struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
+
+ u32 phyif;
+ unsigned int dedicated_fifos:1;
+ unsigned char num_of_eps;
+
+ struct dentry *debug_root;
+ struct dentry *debug_file;
+ struct dentry *debug_fifo;
+
+ struct usb_request *ep0_reply;
+ struct usb_request *ctrl_req;
+ u8 ep0_buff[8];
+ u8 ctrl_buff[8];
+
+ struct usb_gadget gadget;
+ unsigned int setup;
+ unsigned long last_rst;
+ struct s3c_hsotg_ep *eps;
+};
+
+/**
+ * struct s3c_hsotg_req - data transfer request
+ * @req: The USB gadget request
+ * @queue: The list of requests for the endpoint this is queued for.
+ * @in_progress: Has already had size/packets written to core
+ * @mapped: DMA buffer for this request has been mapped via dma_map_single().
+ */
+struct s3c_hsotg_req {
+ struct usb_request req;
+ struct list_head queue;
+ unsigned char in_progress;
+ unsigned char mapped;
+};
+
+#define call_gadget(_hs, _entry) \
+do { \
+ if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
+ (_hs)->driver && (_hs)->driver->_entry) { \
+ spin_unlock(&_hs->lock); \
+ (_hs)->driver->_entry(&(_hs)->gadget); \
+ spin_lock(&_hs->lock); \
+ } \
+} while (0)
+
struct dwc2_hsotg;
struct dwc2_host_chan;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
new file mode 100644
index 00000000000..f3c56a2fed5
--- /dev/null
+++ b/drivers/usb/dwc2/gadget.c
@@ -0,0 +1,3673 @@
+/**
+ * linux/drivers/usb/gadget/s3c-hsotg.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C USB2.0 High-speed / OtG driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/phy.h>
+#include <linux/platform_data/s3c-hsotg.h>
+
+#include "core.h"
+
+/* conversion functions */
+static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
+{
+ return container_of(req, struct s3c_hsotg_req, req);
+}
+
+static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct s3c_hsotg_ep, ep);
+}
+
+static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct s3c_hsotg, gadget);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+ writel(readl(ptr) | val, ptr);
+}
+
+static inline void __bic32(void __iomem *ptr, u32 val)
+{
+ writel(readl(ptr) & ~val, ptr);
+}
+
+/* forward decleration of functions */
+static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
+
+/**
+ * using_dma - return the DMA status of the driver.
+ * @hsotg: The driver state.
+ *
+ * Return true if we're using DMA.
+ *
+ * Currently, we have the DMA support code worked into everywhere
+ * that needs it, but the AMBA DMA implementation in the hardware can
+ * only DMA from 32bit aligned addresses. This means that gadgets such
+ * as the CDC Ethernet cannot work as they often pass packets which are
+ * not 32bit aligned.
+ *
+ * Unfortunately the choice to use DMA or not is global to the controller
+ * and seems to be only settable when the controller is being put through
+ * a core reset. This means we either need to fix the gadgets to take
+ * account of DMA alignment, or add bounce buffers (yuerk).
+ *
+ * Until this issue is sorted out, we always return 'false'.
+ */
+static inline bool using_dma(struct s3c_hsotg *hsotg)
+{
+ return false; /* support is not complete */
+}
+
+/**
+ * s3c_hsotg_en_gsint - enable one or more of the general interrupt
+ * @hsotg: The device state
+ * @ints: A bitmask of the interrupts to enable
+ */
+static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
+{
+ u32 gsintmsk = readl(hsotg->regs + GINTMSK);
+ u32 new_gsintmsk;
+
+ new_gsintmsk = gsintmsk | ints;
+
+ if (new_gsintmsk != gsintmsk) {
+ dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk);
+ writel(new_gsintmsk, hsotg->regs + GINTMSK);
+ }
+}
+
+/**
+ * s3c_hsotg_disable_gsint - disable one or more of the general interrupt
+ * @hsotg: The device state
+ * @ints: A bitmask of the interrupts to enable
+ */
+static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
+{
+ u32 gsintmsk = readl(hsotg->regs + GINTMSK);
+ u32 new_gsintmsk;
+
+ new_gsintmsk = gsintmsk & ~ints;
+
+ if (new_gsintmsk != gsintmsk)
+ writel(new_gsintmsk, hsotg->regs + GINTMSK);
+}
+
+/**
+ * s3c_hsotg_ctrl_epint - enable/disable an endpoint irq
+ * @hsotg: The device state
+ * @ep: The endpoint index
+ * @dir_in: True if direction is in.
+ * @en: The enable value, true to enable
+ *
+ * Set or clear the mask for an individual endpoint's interrupt
+ * request.
+ */
+static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
+ unsigned int ep, unsigned int dir_in,
+ unsigned int en)
+{
+ unsigned long flags;
+ u32 bit = 1 << ep;
+ u32 daint;
+
+ if (!dir_in)
+ bit <<= 16;
+
+ local_irq_save(flags);
+ daint = readl(hsotg->regs + DAINTMSK);
+ if (en)
+ daint |= bit;
+ else
+ daint &= ~bit;
+ writel(daint, hsotg->regs + DAINTMSK);
+ local_irq_restore(flags);
+}
+
+/**
+ * s3c_hsotg_init_fifo - initialise non-periodic FIFOs
+ * @hsotg: The device instance.
+ */
+static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
+{
+ unsigned int ep;
+ unsigned int addr;
+ unsigned int size;
+ int timeout;
+ u32 val;
+
+ /* set FIFO sizes to 2048/1024 */
+
+ writel(2048, hsotg->regs + GRXFSIZ);
+ writel((2048 << FIFOSIZE_STARTADDR_SHIFT) |
+ (1024 << FIFOSIZE_DEPTH_SHIFT), hsotg->regs + GNPTXFSIZ);
+
+ /*
+ * arange all the rest of the TX FIFOs, as some versions of this
+ * block have overlapping default addresses. This also ensures
+ * that if the settings have been changed, then they are set to
+ * known values.
+ */
+
+ /* start at the end of the GNPTXFSIZ, rounded up */
+ addr = 2048 + 1024;
+ size = 768;
+
+ /*
+ * currently we allocate TX FIFOs for all possible endpoints,
+ * and assume that they are all the same size.
+ */
+
+ for (ep = 1; ep <= 15; ep++) {
+ val = addr;
+ val |= size << FIFOSIZE_DEPTH_SHIFT;
+ addr += size;
+
+ writel(val, hsotg->regs + DPTXFSIZN(ep));
+ }
+
+ /*
+ * according to p428 of the design guide, we need to ensure that
+ * all fifos are flushed before continuing
+ */
+
+ writel(GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH |
+ GRSTCTL_RXFFLSH, hsotg->regs + GRSTCTL);
+
+ /* wait until the fifos are both flushed */
+ timeout = 100;
+ while (1) {
+ val = readl(hsotg->regs + GRSTCTL);
+
+ if ((val & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) == 0)
+ break;
+
+ if (--timeout == 0) {
+ dev_err(hsotg->dev,
+ "%s: timeout flushing fifos (GRSTCTL=%08x)\n",
+ __func__, val);
+ }
+
+ udelay(1);
+ }
+
+ dev_dbg(hsotg->dev, "FIFOs reset, timeout at %d\n", timeout);
+}
+
+/**
+ * @ep: USB endpoint to allocate request for.
+ * @flags: Allocation flags
+ *
+ * Allocate a new USB request structure appropriate for the specified endpoint
+ */
+static struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep,
+ gfp_t flags)
+{
+ struct s3c_hsotg_req *req;
+
+ req = kzalloc(sizeof(struct s3c_hsotg_req), flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+/**
+ * is_ep_periodic - return true if the endpoint is in periodic mode.
+ * @hs_ep: The endpoint to query.
+ *
+ * Returns true if the endpoint is in periodic mode, meaning it is being
+ * used for an Interrupt or ISO transfer.
+ */
+static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
+{
+ return hs_ep->periodic;
+}
+
+/**
+ * s3c_hsotg_unmap_dma - unmap the DMA memory being used for the request
+ * @hsotg: The device state.
+ * @hs_ep: The endpoint for the request
+ * @hs_req: The request being processed.
+ *
+ * This is the reverse of s3c_hsotg_map_dma(), called for the completion
+ * of a request to ensure the buffer is ready for access by the caller.
+ */
+static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep,
+ struct s3c_hsotg_req *hs_req)
+{
+ struct usb_request *req = &hs_req->req;
+
+ /* ignore this if we're not moving any data */
+ if (hs_req->req.length == 0)
+ return;
+
+ usb_gadget_unmap_request(&hsotg->gadget, req, hs_ep->dir_in);
+}
+
+/**
+ * s3c_hsotg_write_fifo - write packet Data to the TxFIFO
+ * @hsotg: The controller state.
+ * @hs_ep: The endpoint we're going to write for.
+ * @hs_req: The request to write data for.
+ *
+ * This is called when the TxFIFO has some space in it to hold a new
+ * transmission and we have something to give it. The actual setup of
+ * the data size is done elsewhere, so all we have to do is to actually
+ * write the data.
+ *
+ * The return value is zero if there is more space (or nothing was done)
+ * otherwise -ENOSPC is returned if the FIFO space was used up.
+ *
+ * This routine is only needed for PIO
+ */
+static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep,
+ struct s3c_hsotg_req *hs_req)
+{
+ bool periodic = is_ep_periodic(hs_ep);
+ u32 gnptxsts = readl(hsotg->regs + GNPTXSTS);
+ int buf_pos = hs_req->req.actual;
+ int to_write = hs_ep->size_loaded;
+ void *data;
+ int can_write;
+ int pkt_round;
+ int max_transfer;
+
+ to_write -= (buf_pos - hs_ep->last_load);
+
+ /* if there's nothing to write, get out early */
+ if (to_write == 0)
+ return 0;
+
+ if (periodic && !hsotg->dedicated_fifos) {
+ u32 epsize = readl(hsotg->regs + DIEPTSIZ(hs_ep->index));
+ int size_left;
+ int size_done;
+
+ /*
+ * work out how much data was loaded so we can calculate
+ * how much data is left in the fifo.
+ */
+
+ size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
+
+ /*
+ * if shared fifo, we cannot write anything until the
+ * previous data has been completely sent.
+ */
+ if (hs_ep->fifo_load != 0) {
+ s3c_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP);
+ return -ENOSPC;
+ }
+
+ dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
+ __func__, size_left,
+ hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
+
+ /* how much of the data has moved */
+ size_done = hs_ep->size_loaded - size_left;
+
+ /* how much data is left in the fifo */
+ can_write = hs_ep->fifo_load - size_done;
+ dev_dbg(hsotg->dev, "%s: => can_write1=%d\n",
+ __func__, can_write);
+
+ can_write = hs_ep->fifo_size - can_write;
+ dev_dbg(hsotg->dev, "%s: => can_write2=%d\n",
+ __func__, can_write);
+
+ if (can_write <= 0) {
+ s3c_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP);
+ return -ENOSPC;
+ }
+ } else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
+ can_write = readl(hsotg->regs + DTXFSTS(hs_ep->index));
+
+ can_write &= 0xffff;
+ can_write *= 4;
+ } else {
+ if (GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(gnptxsts) == 0) {
+ dev_dbg(hsotg->dev,
+ "%s: no queue slots available (0x%08x)\n",
+ __func__, gnptxsts);
+
+ s3c_hsotg_en_gsint(hsotg, GINTSTS_NPTXFEMP);
+ return -ENOSPC;
+ }
+
+ can_write = GNPTXSTS_NP_TXF_SPC_AVAIL_GET(gnptxsts);
+ can_write *= 4; /* fifo size is in 32bit quantities. */
+ }
+
+ max_transfer = hs_ep->ep.maxpacket * hs_ep->mc;
+
+ dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n",
+ __func__, gnptxsts, can_write, to_write, max_transfer);
+
+ /*
+ * limit to 512 bytes of data, it seems at least on the non-periodic
+ * FIFO, requests of >512 cause the endpoint to get stuck with a
+ * fragment of the end of the transfer in it.
+ */
+ if (can_write > 512 && !periodic)
+ can_write = 512;
+
+ /*
+ * limit the write to one max-packet size worth of data, but allow
+ * the transfer to return that it did not run out of fifo space
+ * doing it.
+ */
+ if (to_write > max_transfer) {
+ to_write = max_transfer;
+
+ /* it's needed only when we do not use dedicated fifos */
+ if (!hsotg->dedicated_fifos)
+ s3c_hsotg_en_gsint(hsotg,
+ periodic ? GINTSTS_PTXFEMP :
+ GINTSTS_NPTXFEMP);
+ }
+
+ /* see if we can write data */
+
+ if (to_write > can_write) {
+ to_write = can_write;
+ pkt_round = to_write % max_transfer;
+
+ /*
+ * Round the write down to an
+ * exact number of packets.
+ *
+ * Note, we do not currently check to see if we can ever
+ * write a full packet or not to the FIFO.
+ */
+
+ if (pkt_round)
+ to_write -= pkt_round;
+
+ /*
+ * enable correct FIFO interrupt to alert us when there
+ * is more room left.
+ */
+
+ /* it's needed only when we do not use dedicated fifos */
+ if (!hsotg->dedicated_fifos)
+ s3c_hsotg_en_gsint(hsotg,
+ periodic ? GINTSTS_PTXFEMP :
+ GINTSTS_NPTXFEMP);
+ }
+
+ dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
+ to_write, hs_req->req.length, can_write, buf_pos);
+
+ if (to_write <= 0)
+ return -ENOSPC;
+
+ hs_req->req.actual = buf_pos + to_write;
+ hs_ep->total_data += to_write;
+
+ if (periodic)
+ hs_ep->fifo_load += to_write;
+
+ to_write = DIV_ROUND_UP(to_write, 4);
+ data = hs_req->req.buf + buf_pos;
+
+ iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
+
+ return (to_write >= can_write) ? -ENOSPC : 0;
+}
+
+/**
+ * get_ep_limit - get the maximum data legnth for this endpoint
+ * @hs_ep: The endpoint
+ *
+ * Return the maximum data that can be queued in one go on a given endpoint
+ * so that transfers that are too long can be split.
+ */
+static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
+{
+ int index = hs_ep->index;
+ unsigned maxsize;
+ unsigned maxpkt;
+
+ if (index != 0) {
+ maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1;
+ maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1;
+ } else {
+ maxsize = 64+64;
+ if (hs_ep->dir_in)
+ maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1;
+ else
+ maxpkt = 2;
+ }
+
+ /* we made the constant loading easier above by using +1 */
+ maxpkt--;
+ maxsize--;
+
+ /*
+ * constrain by packet count if maxpkts*pktsize is greater
+ * than the length register size.
+ */
+
+ if ((maxpkt * hs_ep->ep.maxpacket) < maxsize)
+ maxsize = maxpkt * hs_ep->ep.maxpacket;
+
+ return maxsize;
+}
+
+/**
+ * s3c_hsotg_start_req - start a USB request from an endpoint's queue
+ * @hsotg: The controller state.
+ * @hs_ep: The endpoint to process a request for
+ * @hs_req: The request to start.
+ * @continuing: True if we are doing more for the current request.
+ *
+ * Start the given request running by setting the endpoint registers
+ * appropriately, and writing any data to the FIFOs.
+ */
+static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep,
+ struct s3c_hsotg_req *hs_req,
+ bool continuing)
+{
+ struct usb_request *ureq = &hs_req->req;
+ int index = hs_ep->index;
+ int dir_in = hs_ep->dir_in;
+ u32 epctrl_reg;
+ u32 epsize_reg;
+ u32 epsize;
+ u32 ctrl;
+ unsigned length;
+ unsigned packets;
+ unsigned maxreq;
+
+ if (index != 0) {
+ if (hs_ep->req && !continuing) {
+ dev_err(hsotg->dev, "%s: active request\n", __func__);
+ WARN_ON(1);
+ return;
+ } else if (hs_ep->req != hs_req && continuing) {
+ dev_err(hsotg->dev,
+ "%s: continue different req\n", __func__);
+ WARN_ON(1);
+ return;
+ }
+ }
+
+ epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
+ epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
+
+ dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n",
+ __func__, readl(hsotg->regs + epctrl_reg), index,
+ hs_ep->dir_in ? "in" : "out");
+
+ /* If endpoint is stalled, we will restart request later */
+ ctrl = readl(hsotg->regs + epctrl_reg);
+
+ if (ctrl & DXEPCTL_STALL) {
+ dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
+ return;
+ }
+
+ length = ureq->length - ureq->actual;
+ dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
+ ureq->length, ureq->actual);
+ if (0)
+ dev_dbg(hsotg->dev,
+ "REQ buf %p len %d dma %pad noi=%d zp=%d snok=%d\n",
+ ureq->buf, length, &ureq->dma,
+ ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
+
+ maxreq = get_ep_limit(hs_ep);
+ if (length > maxreq) {
+ int round = maxreq % hs_ep->ep.maxpacket;
+
+ dev_dbg(hsotg->dev, "%s: length %d, max-req %d, r %d\n",
+ __func__, length, maxreq, round);
+
+ /* round down to multiple of packets */
+ if (round)
+ maxreq -= round;
+
+ length = maxreq;
+ }
+
+ if (length)
+ packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket);
+ else
+ packets = 1; /* send one packet if length is zero. */
+
+ if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) {
+ dev_err(hsotg->dev, "req length > maxpacket*mc\n");
+ return;
+ }
+
+ if (dir_in && index != 0)
+ if (hs_ep->isochronous)
+ epsize = DXEPTSIZ_MC(packets);
+ else
+ epsize = DXEPTSIZ_MC(1);
+ else
+ epsize = 0;
+
+ if (index != 0 && ureq->zero) {
+ /*
+ * test for the packets being exactly right for the
+ * transfer
+ */
+
+ if (length == (packets * hs_ep->ep.maxpacket))
+ packets++;
+ }
+
+ epsize |= DXEPTSIZ_PKTCNT(packets);
+ epsize |= DXEPTSIZ_XFERSIZE(length);
+
+ dev_dbg(hsotg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n",
+ __func__, packets, length, ureq->length, epsize, epsize_reg);
+
+ /* store the request as the current one we're doing */
+ hs_ep->req = hs_req;
+
+ /* write size / packets */
+ writel(epsize, hsotg->regs + epsize_reg);
+
+ if (using_dma(hsotg) && !continuing) {
+ unsigned int dma_reg;
+
+ /*
+ * write DMA address to control register, buffer already
+ * synced by s3c_hsotg_ep_queue().
+ */
+
+ dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
+ writel(ureq->dma, hsotg->regs + dma_reg);
+
+ dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n",
+ __func__, &ureq->dma, dma_reg);
+ }
+
+ ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
+ ctrl |= DXEPCTL_USBACTEP;
+
+ dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup);
+
+ /* For Setup request do not clear NAK */
+ if (hsotg->setup && index == 0)
+ hsotg->setup = 0;
+ else
+ ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
+
+
+ dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
+ writel(ctrl, hsotg->regs + epctrl_reg);
+
+ /*
+ * set these, it seems that DMA support increments past the end
+ * of the packet buffer so we need to calculate the length from
+ * this information.
+ */
+ hs_ep->size_loaded = length;
+ hs_ep->last_load = ureq->actual;
+
+ if (dir_in && !using_dma(hsotg)) {
+ /* set these anyway, we may need them for non-periodic in */
+ hs_ep->fifo_load = 0;
+
+ s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req);
+ }
+
+ /*
+ * clear the INTknTXFEmpMsk when we start request, more as a aide
+ * to debugging to see what is going on.
+ */
+ if (dir_in)
+ writel(DIEPMSK_INTKNTXFEMPMSK,
+ hsotg->regs + DIEPINT(index));
+
+ /*