* [RFC v2 0/5] Platform driver support for 'amd5536udc' driver
From: Raviteja Garimella @ 2017-01-17 8:05 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
This is RFC for the changes made as per the review comments made for
the previous version. I would like to know if this approach (the way
the driver is split and the naming and all)looks good to be submitted.
Changes from v1:
===============
1. Split the driver into platform/pci specific drivers with a core driver
file that handles the common driver routines that are exported.
2. Split the driver into number of patches as suggested in previous
review comments.
3. Added the devicetree bindings documentation for Synopsys platform
driver.
Introduction from previous version:
===================================
This patch adds platform device support to the existing 'amd5536udc'
driver.
The UDC is based on Synopsys Designware core USB (2.0) Device controller
IP.
The driver so far supports UDCs that are a part of AMD southbridge
and is connected through PCI bus.
The same driver can be used with UDCs that are integrated into SoCs
like Broadcom's Northstar2/Cygnus platforms by adding platform device
suooprt.
This patch contains all the changes that were required to get the driver
functional on Broadcom's Northstar2 platform.
This is a request for comments from maintainers/others regarding approach
on whether to have 2 different drivers (one each for AMD and Broadcom)
with a common library (3 files in total), or have a single driver like
it's done in this patch and have the driver filename changed to some
common name based on ther underlying IP, like snps_udc.c.
Below are the main changes done:
1. Added OF based platform device registration -- so that the driver gets
probed based on the device tree entry. Like wise, remove routine and
platform PM ops are supported.
2. Modified debug prints to be compatible with both pci and platform
devices.
3. Added members to 'struct udc' in header file for extcon and phy support.
This is required if the UDC is connected to a Dual Role Device Phy
where the Phy can be configured to be in Device mode or Host mode based
on the type of external cable that is connected to the port.
4. Added checks in udc connect/disconnect routines so as to return if the
routine is already called.
5. Modified the arguments passed to dma_pool_create routine -- which
expects struct device, whereas NULL is passed in the existing version.
6. Kconfig changes are done so that the driver now depends on either of
CONFIG_OF or CONFIG_PCI. More description about the Synopsys IP is
provided.
Raviteja Garimella (5):
UDC: Split the driver into amd (pci) and Synopsys core driver
UDC: make debug prints compatible with both pci and platform devices
UDC: Provide correct arguments for 'dma_pool_create'
DT bindings documentation for Synopsys UDC platform driver
UDC: Add Synopsys UDC Platform driver
.../devicetree/bindings/usb/snps,dw-ahb-udc.txt | 27 +
drivers/usb/gadget/udc/Kconfig | 32 +
drivers/usb/gadget/udc/Makefile | 2 +
drivers/usb/gadget/udc/amd5536udc.c | 3249 +-------------------
drivers/usb/gadget/udc/amd5536udc.h | 54 +-
drivers/usb/gadget/udc/snps_udc_core.c | 3232 +++++++++++++++++++
drivers/usb/gadget/udc/snps_udc_plat.c | 342 +++
7 files changed, 3714 insertions(+), 3224 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/snps,dw-ahb-udc.txt
create mode 100644 drivers/usb/gadget/udc/snps_udc_core.c
create mode 100644 drivers/usb/gadget/udc/snps_udc_plat.c
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RFC v2 1/5] UDC: Split the driver into amd (pci) and Synopsys core driver
From: Raviteja Garimella @ 2017-01-17 8:05 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484640308-25976-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
This patch splits the amd5536udc driver into two -- one that does
pci device registration and the other file that does the rest of
the driver tasks like the gadget/ep ops etc for Synopsys UDC.
This way of splitting helps in exporting core driver symbols which
can be used by any other platform/pci driver that is written for
the same Synopsys USB device controller.
The current patch also includes a change in the Kconfig and Makefile.
A new config option USB_SNP_CORE will be selected automatically when
any one of the platform or pci driver for the same UDC is selected.
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
drivers/usb/gadget/udc/Kconfig | 17 +
drivers/usb/gadget/udc/Makefile | 1 +
drivers/usb/gadget/udc/amd5536udc.c | 3248 +-------------------------------
drivers/usb/gadget/udc/amd5536udc.h | 36 +
drivers/usb/gadget/udc/snps_udc_core.c | 3216 +++++++++++++++++++++++++++++++
5 files changed, 3295 insertions(+), 3223 deletions(-)
create mode 100644 drivers/usb/gadget/udc/snps_udc_core.c
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 658b8da..9d889bb 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -239,6 +239,19 @@ config USB_MV_U3D
MARVELL PXA2128 Processor series include a super speed USB3.0 device
controller, which support super speed USB peripheral.
+config USB_SNP_CORE
+ tristate
+ help
+ This enables core driver support for Synopsys USB 2.0 Device
+ controller.
+
+ This will be enabled when PCI or Platform driver for this UDC is
+ selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
+ USB_AMD5536UDC options.
+
+ This IP is different to the High Speed OTG IP that can be enabled
+ by selecting USB_DWC2 or USB_DWC3 options.
+
#
# Controllers available in both integrated and discrete versions
#
@@ -263,6 +276,7 @@ source "drivers/usb/gadget/udc/bdc/Kconfig"
config USB_AMD5536UDC
tristate "AMD5536 UDC"
+ select USB_SNP_CORE
depends on PCI
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
@@ -271,6 +285,9 @@ config USB_AMD5536UDC
The UDC port supports OTG operation, and may be used as a host port
if it's not being used to implement peripheral or OTG roles.
+ This UDC is based on Synopsys USB device controller IP and selects
+ CONFIG_USB_SNP_CORE option to build the core driver.
+
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked.
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 98e74ed..3929f6f1 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
+obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index ea03ca7..c0acb80 100644
--- a/drivers/usb/gadget/udc/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
@@ -22,3103 +22,37 @@
* UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
* work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
* can be used with gadget ether.
- */
-
-/* debug control */
-/* #define UDC_VERBOSE */
-
-/* Driver strings */
-#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
-#define UDC_DRIVER_VERSION_STRING "01.00.0206"
-
-/* system */
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/ioctl.h>
-#include <linux/fs.h>
-#include <linux/dmapool.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/prefetch.h>
-
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-/* gadget stack */
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-/* udc specific */
-#include "amd5536udc.h"
-
-
-static void udc_tasklet_disconnect(unsigned long);
-static void empty_req_queue(struct udc_ep *);
-static void udc_setup_endpoints(struct udc *dev);
-static void udc_soft_reset(struct udc *dev);
-static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
-static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
-
-/* description */
-static const char mod_desc[] = UDC_MOD_DESCRIPTION;
-static const char name[] = "amd5536udc";
-
-/* structure to hold endpoint function pointers */
-static const struct usb_ep_ops udc_ep_ops;
-
-/* received setup data */
-static union udc_setup_data setup_data;
-
-/* pointer to device object */
-static struct udc *udc;
-
-/* irq spin lock for soft reset */
-static DEFINE_SPINLOCK(udc_irq_spinlock);
-/* stall spin lock */
-static DEFINE_SPINLOCK(udc_stall_spinlock);
-
-/*
-* slave mode: pending bytes in rx fifo after nyet,
-* used if EPIN irq came but no req was available
-*/
-static unsigned int udc_rxfifo_pending;
-
-/* count soft resets after suspend to avoid loop */
-static int soft_reset_occured;
-static int soft_reset_after_usbreset_occured;
-
-/* timer */
-static struct timer_list udc_timer;
-static int stop_timer;
-
-/* set_rde -- Is used to control enabling of RX DMA. Problem is
- * that UDC has only one bit (RDE) to enable/disable RX DMA for
- * all OUT endpoints. So we have to handle race conditions like
- * when OUT data reaches the fifo but no request was queued yet.
- * This cannot be solved by letting the RX DMA disabled until a
- * request gets queued because there may be other OUT packets
- * in the FIFO (important for not blocking control traffic).
- * The value of set_rde controls the correspondig timer.
- *
- * set_rde -1 == not used, means it is alloed to be set to 0 or 1
- * set_rde 0 == do not touch RDE, do no start the RDE timer
- * set_rde 1 == timer function will look whether FIFO has data
- * set_rde 2 == set by timer function to enable RX DMA on next call
- */
-static int set_rde = -1;
-
-static DECLARE_COMPLETION(on_exit);
-static struct timer_list udc_pollstall_timer;
-static int stop_pollstall_timer;
-static DECLARE_COMPLETION(on_pollstall_exit);
-
-/* tasklet for usb disconnect */
-static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
- (unsigned long) &udc);
-
-
-/* endpoint names used for print */
-static const char ep0_string[] = "ep0in";
-static const struct {
- const char *name;
- const struct usb_ep_caps caps;
-} ep_info[] = {
-#define EP_INFO(_name, _caps) \
- { \
- .name = _name, \
- .caps = _caps, \
- }
-
- EP_INFO(ep0_string,
- USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep1in-int",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep2in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep3in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep4in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep5in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep6in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep7in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep8in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep9in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep10in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep11in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep12in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep13in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep14in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep15in-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep0out",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep1out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep2out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep3out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep4out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep5out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep6out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep7out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep8out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep9out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep10out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep11out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep12out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep13out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep14out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep15out-bulk",
- USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
-
-#undef EP_INFO
-};
-
-/* DMA usage flag */
-static bool use_dma = 1;
-/* packet per buffer dma */
-static bool use_dma_ppb = 1;
-/* with per descr. update */
-static bool use_dma_ppb_du;
-/* buffer fill mode */
-static int use_dma_bufferfill_mode;
-/* full speed only mode */
-static bool use_fullspeed;
-/* tx buffer size for high speed */
-static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
-
-/* module parameters */
-module_param(use_dma, bool, S_IRUGO);
-MODULE_PARM_DESC(use_dma, "true for DMA");
-module_param(use_dma_ppb, bool, S_IRUGO);
-MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
-module_param(use_dma_ppb_du, bool, S_IRUGO);
-MODULE_PARM_DESC(use_dma_ppb_du,
- "true for DMA in packet per buffer mode with descriptor update");
-module_param(use_fullspeed, bool, S_IRUGO);
-MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
-
-/*---------------------------------------------------------------------------*/
-/* Prints UDC device registers and endpoint irq registers */
-static void print_regs(struct udc *dev)
-{
- DBG(dev, "------- Device registers -------\n");
- DBG(dev, "dev config = %08x\n", readl(&dev->regs->cfg));
- DBG(dev, "dev control = %08x\n", readl(&dev->regs->ctl));
- DBG(dev, "dev status = %08x\n", readl(&dev->regs->sts));
- DBG(dev, "\n");
- DBG(dev, "dev int's = %08x\n", readl(&dev->regs->irqsts));
- DBG(dev, "dev intmask = %08x\n", readl(&dev->regs->irqmsk));
- DBG(dev, "\n");
- DBG(dev, "dev ep int's = %08x\n", readl(&dev->regs->ep_irqsts));
- DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk));
- DBG(dev, "\n");
- DBG(dev, "USE DMA = %d\n", use_dma);
- if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
- DBG(dev, "DMA mode = PPBNDU (packet per buffer "
- "WITHOUT desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
- } else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
- DBG(dev, "DMA mode = PPBDU (packet per buffer "
- "WITH desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
- }
- if (use_dma && use_dma_bufferfill_mode) {
- DBG(dev, "DMA mode = BF (buffer fill mode)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
- }
- if (!use_dma)
- dev_info(&dev->pdev->dev, "FIFO mode\n");
- DBG(dev, "-------------------------------------------------------\n");
-}
-
-/* Masks unused interrupts */
-static int udc_mask_unused_interrupts(struct udc *dev)
-{
- u32 tmp;
-
- /* mask all dev interrupts */
- tmp = AMD_BIT(UDC_DEVINT_SVC) |
- AMD_BIT(UDC_DEVINT_ENUM) |
- AMD_BIT(UDC_DEVINT_US) |
- AMD_BIT(UDC_DEVINT_UR) |
- AMD_BIT(UDC_DEVINT_ES) |
- AMD_BIT(UDC_DEVINT_SI) |
- AMD_BIT(UDC_DEVINT_SOF)|
- AMD_BIT(UDC_DEVINT_SC);
- writel(tmp, &dev->regs->irqmsk);
-
- /* mask all ep interrupts */
- writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);
-
- return 0;
-}
-
-/* Enables endpoint 0 interrupts */
-static int udc_enable_ep0_interrupts(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "udc_enable_ep0_interrupts()\n");
-
- /* read irq mask */
- tmp = readl(&dev->regs->ep_irqmsk);
- /* enable ep0 irq's */
- tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)
- & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);
- writel(tmp, &dev->regs->ep_irqmsk);
-
- return 0;
-}
-
-/* Enables device interrupts for SET_INTF and SET_CONFIG */
-static int udc_enable_dev_setup_interrupts(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "enable device interrupts for setup data\n");
-
- /* read irq mask */
- tmp = readl(&dev->regs->irqmsk);
-
- /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
- tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)
- & AMD_UNMASK_BIT(UDC_DEVINT_SC)
- & AMD_UNMASK_BIT(UDC_DEVINT_UR)
- & AMD_UNMASK_BIT(UDC_DEVINT_SVC)
- & AMD_UNMASK_BIT(UDC_DEVINT_ENUM);
- writel(tmp, &dev->regs->irqmsk);
-
- return 0;
-}
-
-/* Calculates fifo start of endpoint based on preceding endpoints */
-static int udc_set_txfifo_addr(struct udc_ep *ep)
-{
- struct udc *dev;
- u32 tmp;
- int i;
-
- if (!ep || !(ep->in))
- return -EINVAL;
-
- dev = ep->dev;
- ep->txfifo = dev->txfifo;
-
- /* traverse ep's */
- for (i = 0; i < ep->num; i++) {
- if (dev->ep[i].regs) {
- /* read fifo size */
- tmp = readl(&dev->ep[i].regs->bufin_framenum);
- tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
- ep->txfifo += tmp;
- }
- }
- return 0;
-}
-
-/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */
-static u32 cnak_pending;
-
-static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num)
-{
- if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) {
- DBG(ep->dev, "NAK could not be cleared for ep%d\n", num);
- cnak_pending |= 1 << (num);
- ep->naking = 1;
- } else
- cnak_pending = cnak_pending & (~(1 << (num)));
-}
-
-
-/* Enables endpoint, is called by gadget driver */
-static int
-udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
-{
- struct udc_ep *ep;
- struct udc *dev;
- u32 tmp;
- unsigned long iflags;
- u8 udc_csr_epix;
- unsigned maxpacket;
-
- if (!usbep
- || usbep->name == ep0_string
- || !desc
- || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
-
- ep = container_of(usbep, struct udc_ep, ep);
- dev = ep->dev;
-
- DBG(dev, "udc_ep_enable() ep %d\n", ep->num);
-
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- spin_lock_irqsave(&dev->lock, iflags);
- ep->ep.desc = desc;
-
- ep->halted = 0;
-
- /* set traffic type */
- tmp = readl(&dev->ep[ep->num].regs->ctl);
- tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
- writel(tmp, &dev->ep[ep->num].regs->ctl);
-
- /* set max packet size */
- maxpacket = usb_endpoint_maxp(desc);
- tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
- tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
- ep->ep.maxpacket = maxpacket;
- writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);
-
- /* IN ep */
- if (ep->in) {
-
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num;
-
- /* set buffer size (tx fifo entries) */
- tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);
- /* double buffering: fifo size = 2 x max packet size */
- tmp = AMD_ADDBITS(
- tmp,
- maxpacket * UDC_EPIN_BUFF_SIZE_MULT
- / UDC_DWORD_BYTES,
- UDC_EPIN_BUFF_SIZE);
- writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);
-
- /* calc. tx fifo base addr */
- udc_set_txfifo_addr(ep);
-
- /* flush fifo */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_F);
- writel(tmp, &ep->regs->ctl);
-
- /* OUT ep */
- } else {
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
-
- /* set max packet size UDC CSR */
- tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
- tmp = AMD_ADDBITS(tmp, maxpacket,
- UDC_CSR_NE_MAX_PKT);
- writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
-
- if (use_dma && !ep->in) {
- /* alloc and init BNA dummy request */
- ep->bna_dummy_req = udc_alloc_bna_dummy(ep);
- ep->bna_occurred = 0;
- }
-
- if (ep->num != UDC_EP0OUT_IX)
- dev->data_ep_enabled = 1;
- }
-
- /* set ep values */
- tmp = readl(&dev->csr->ne[udc_csr_epix]);
- /* max packet */
- tmp = AMD_ADDBITS(tmp, maxpacket, UDC_CSR_NE_MAX_PKT);
- /* ep number */
- tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
- /* ep direction */
- tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);
- /* ep type */
- tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
- /* ep config */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
- /* ep interface */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
- /* ep alt */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
- /* write reg */
- writel(tmp, &dev->csr->ne[udc_csr_epix]);
-
- /* enable ep irq */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp &= AMD_UNMASK_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
-
- /*
- * clear NAK by writing CNAK
- * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written
- */
- if (!use_dma || ep->in) {
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
- tmp = desc->bEndpointAddress;
- DBG(dev, "%s enabled\n", usbep->name);
-
- spin_unlock_irqrestore(&dev->lock, iflags);
- return 0;
-}
-
-/* Resets endpoint */
-static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
-{
- u32 tmp;
-
- VDBG(ep->dev, "ep-%d reset\n", ep->num);
- ep->ep.desc = NULL;
- ep->ep.ops = &udc_ep_ops;
- INIT_LIST_HEAD(&ep->queue);
-
- usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0);
- /* set NAK */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_SNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 1;
-
- /* disable interrupt */
- tmp = readl(®s->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp, ®s->ep_irqmsk);
-
- if (ep->in) {
- /* unset P and IN bit of potential former DMA */
- tmp = readl(&ep->regs->ctl);
- tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);
- writel(tmp, &ep->regs->ctl);
-
- tmp = readl(&ep->regs->sts);
- tmp |= AMD_BIT(UDC_EPSTS_IN);
- writel(tmp, &ep->regs->sts);
-
- /* flush the fifo */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_F);
- writel(tmp, &ep->regs->ctl);
-
- }
- /* reset desc pointer */
- writel(0, &ep->regs->desptr);
-}
-
-/* Disables endpoint, is called by gadget driver */
-static int udc_ep_disable(struct usb_ep *usbep)
-{
- struct udc_ep *ep = NULL;
- unsigned long iflags;
-
- if (!usbep)
- return -EINVAL;
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (usbep->name == ep0_string || !ep->ep.desc)
- return -EINVAL;
-
- DBG(ep->dev, "Disable ep-%d\n", ep->num);
-
- spin_lock_irqsave(&ep->dev->lock, iflags);
- udc_free_request(&ep->ep, &ep->bna_dummy_req->req);
- empty_req_queue(ep);
- ep_init(ep->dev->regs, ep);
- spin_unlock_irqrestore(&ep->dev->lock, iflags);
-
- return 0;
-}
-
-/* Allocates request packet, called by gadget driver */
-static struct usb_request *
-udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
-{
- struct udc_request *req;
- struct udc_data_dma *dma_desc;
- struct udc_ep *ep;
-
- if (!usbep)
- return NULL;
-
- ep = container_of(usbep, struct udc_ep, ep);
-
- VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num);
- req = kzalloc(sizeof(struct udc_request), gfp);
- if (!req)
- return NULL;
-
- req->req.dma = DMA_DONT_USE;
- INIT_LIST_HEAD(&req->queue);
-
- if (ep->dma) {
- /* ep0 in requests are allocated from data pool here */
- dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
- &req->td_phys);
- if (!dma_desc) {
- kfree(req);
- return NULL;
- }
-
- VDBG(ep->dev, "udc_alloc_req: req = %p dma_desc = %p, "
- "td_phys = %lx\n",
- req, dma_desc,
- (unsigned long)req->td_phys);
- /* prevent from using desc. - set HOST BUSY */
- dma_desc->status = AMD_ADDBITS(dma_desc->status,
- UDC_DMA_STP_STS_BS_HOST_BUSY,
- UDC_DMA_STP_STS_BS);
- dma_desc->bufptr = cpu_to_le32(DMA_DONT_USE);
- req->td_data = dma_desc;
- req->td_data_last = NULL;
- req->chain_len = 1;
- }
-
- return &req->req;
-}
-
-/* frees pci pool descriptors of a DMA chain */
-static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
-{
- int ret_val = 0;
- struct udc_data_dma *td;
- struct udc_data_dma *td_last = NULL;
- unsigned int i;
-
- DBG(dev, "free chain req = %p\n", req);
-
- /* do not free first desc., will be done by free for request */
- td_last = req->td_data;
- td = phys_to_virt(td_last->next);
-
- for (i = 1; i < req->chain_len; i++) {
- pci_pool_free(dev->data_requests, td,
- (dma_addr_t)td_last->next);
- td_last = td;
- td = phys_to_virt(td_last->next);
- }
-
- return ret_val;
-}
-
-/* Frees request packet, called by gadget driver */
-static void
-udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
-{
- struct udc_ep *ep;
- struct udc_request *req;
-
- if (!usbep || !usbreq)
- return;
-
- ep = container_of(usbep, struct udc_ep, ep);
- req = container_of(usbreq, struct udc_request, req);
- VDBG(ep->dev, "free_req req=%p\n", req);
- BUG_ON(!list_empty(&req->queue));
- if (req->td_data) {
- VDBG(ep->dev, "req->td_data=%p\n", req->td_data);
-
- /* free dma chain if created */
- if (req->chain_len > 1)
- udc_free_dma_chain(ep->dev, req);
-
- pci_pool_free(ep->dev->data_requests, req->td_data,
- req->td_phys);
- }
- kfree(req);
-}
-
-/* Init BNA dummy descriptor for HOST BUSY and pointing to itself */
-static void udc_init_bna_dummy(struct udc_request *req)
-{
- if (req) {
- /* set last bit */
- req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
- /* set next pointer to itself */
- req->td_data->next = req->td_phys;
- /* set HOST BUSY */
- req->td_data->status
- = AMD_ADDBITS(req->td_data->status,
- UDC_DMA_STP_STS_BS_DMA_DONE,
- UDC_DMA_STP_STS_BS);
-#ifdef UDC_VERBOSE
- pr_debug("bna desc = %p, sts = %08x\n",
- req->td_data, req->td_data->status);
-#endif
- }
-}
-
-/* Allocate BNA dummy descriptor */
-static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep)
-{
- struct udc_request *req = NULL;
- struct usb_request *_req = NULL;
-
- /* alloc the dummy request */
- _req = udc_alloc_request(&ep->ep, GFP_ATOMIC);
- if (_req) {
- req = container_of(_req, struct udc_request, req);
- ep->bna_dummy_req = req;
- udc_init_bna_dummy(req);
- }
- return req;
-}
-
-/* Write data to TX fifo for IN packets */
-static void
-udc_txfifo_write(struct udc_ep *ep, struct usb_request *req)
-{
- u8 *req_buf;
- u32 *buf;
- int i, j;
- unsigned bytes = 0;
- unsigned remaining = 0;
-
- if (!req || !ep)
- return;
-
- req_buf = req->buf + req->actual;
- prefetch(req_buf);
- remaining = req->length - req->actual;
-
- buf = (u32 *) req_buf;
-
- bytes = ep->ep.maxpacket;
- if (bytes > remaining)
- bytes = remaining;
-
- /* dwords first */
- for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
- writel(*(buf + i), ep->txfifo);
-
- /* remaining bytes must be written by byte access */
- for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
- writeb((u8)(*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)),
- ep->txfifo);
- }
-
- /* dummy write confirm */
- writel(0, &ep->regs->confirm);
-}
-
-/* Read dwords from RX fifo for OUT transfers */
-static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords)
-{
- int i;
-
- VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords);
-
- for (i = 0; i < dwords; i++)
- *(buf + i) = readl(dev->rxfifo);
- return 0;
-}
-
-/* Read bytes from RX fifo for OUT transfers */
-static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes)
-{
- int i, j;
- u32 tmp;
-
- VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes);
-
- /* dwords first */
- for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
- *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo);
-
- /* remaining bytes must be read by byte access */
- if (bytes % UDC_DWORD_BYTES) {
- tmp = readl(dev->rxfifo);
- for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
- *(buf + (i<<2) + j) = (u8)(tmp & UDC_BYTE_MASK);
- tmp = tmp >> UDC_BITS_PER_BYTE;
- }
- }
-
- return 0;
-}
-
-/* Read data from RX fifo for OUT transfers */
-static int
-udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req)
-{
- u8 *buf;
- unsigned buf_space;
- unsigned bytes = 0;
- unsigned finished = 0;
-
- /* received number bytes */
- bytes = readl(&ep->regs->sts);
- bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE);
-
- buf_space = req->req.length - req->req.actual;
- buf = req->req.buf + req->req.actual;
- if (bytes > buf_space) {
- if ((buf_space % ep->ep.maxpacket) != 0) {
- DBG(ep->dev,
- "%s: rx %d bytes, rx-buf space = %d bytesn\n",
- ep->ep.name, bytes, buf_space);
- req->req.status = -EOVERFLOW;
- }
- bytes = buf_space;
- }
- req->req.actual += bytes;
-
- /* last packet ? */
- if (((bytes % ep->ep.maxpacket) != 0) || (!bytes)
- || ((req->req.actual == req->req.length) && !req->req.zero))
- finished = 1;
-
- /* read rx fifo bytes */
- VDBG(ep->dev, "ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes);
- udc_rxfifo_read_bytes(ep->dev, buf, bytes);
-
- return finished;
-}
-
-/* Creates or re-inits a DMA chain */
-static int udc_create_dma_chain(
- struct udc_ep *ep,
- struct udc_request *req,
- unsigned long buf_len, gfp_t gfp_flags
-)
-{
- unsigned long bytes = req->req.length;
- unsigned int i;
- dma_addr_t dma_addr;
- struct udc_data_dma *td = NULL;
- struct udc_data_dma *last = NULL;
- unsigned long txbytes;
- unsigned create_new_chain = 0;
- unsigned len;
-
- VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n",
- bytes, buf_len);
- dma_addr = DMA_DONT_USE;
-
- /* unset L bit in first desc for OUT */
- if (!ep->in)
- req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
-
- /* alloc only new desc's if not already available */
- len = req->req.length / ep->ep.maxpacket;
- if (req->req.length % ep->ep.maxpacket)
- len++;
-
- if (len > req->chain_len) {
- /* shorter chain already allocated before */
- if (req->chain_len > 1)
- udc_free_dma_chain(ep->dev, req);
- req->chain_len = len;
- create_new_chain = 1;
- }
-
- td = req->td_data;
- /* gen. required number of descriptors and buffers */
- for (i = buf_len; i < bytes; i += buf_len) {
- /* create or determine next desc. */
- if (create_new_chain) {
- td = pci_pool_alloc(ep->dev->data_requests,
- gfp_flags, &dma_addr);
- if (!td)
- return -ENOMEM;
-
- td->status = 0;
- } else if (i == buf_len) {
- /* first td */
- td = (struct udc_data_dma *)phys_to_virt(
- req->td_data->next);
- td->status = 0;
- } else {
- td = (struct udc_data_dma *)phys_to_virt(last->next);
- td->status = 0;
- }
-
- if (td)
- td->bufptr = req->req.dma + i; /* assign buffer */
- else
- break;
-
- /* short packet ? */
- if ((bytes - i) >= buf_len) {
- txbytes = buf_len;
- } else {
- /* short packet */
- txbytes = bytes - i;
- }
-
- /* link td and assign tx bytes */
- if (i == buf_len) {
- if (create_new_chain)
- req->td_data->next = dma_addr;
- /*
- * else
- * req->td_data->next = virt_to_phys(td);
- */
- /* write tx bytes */
- if (ep->in) {
- /* first desc */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- ep->ep.maxpacket,
- UDC_DMA_IN_STS_TXBYTES);
- /* second desc */
- td->status = AMD_ADDBITS(td->status,
- txbytes,
- UDC_DMA_IN_STS_TXBYTES);
- }
- } else {
- if (create_new_chain)
- last->next = dma_addr;
- /*
- * else
- * last->next = virt_to_phys(td);
- */
- if (ep->in) {
- /* write tx bytes */
- td->status = AMD_ADDBITS(td->status,
- txbytes,
- UDC_DMA_IN_STS_TXBYTES);
- }
- }
- last = td;
- }
- /* set last bit */
- if (td) {
- td->status |= AMD_BIT(UDC_DMA_IN_STS_L);
- /* last desc. points to itself */
- req->td_data_last = td;
- }
-
- return 0;
-}
-
-/* create/re-init a DMA descriptor or a DMA descriptor chain */
-static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp)
-{
- int retval = 0;
- u32 tmp;
-
- VDBG(ep->dev, "prep_dma\n");
- VDBG(ep->dev, "prep_dma ep%d req->td_data=%p\n",
- ep->num, req->td_data);
-
- /* set buffer pointer */
- req->td_data->bufptr = req->req.dma;
-
- /* set last bit */
- req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
-
- /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */
- if (use_dma_ppb) {
-
- retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
- if (retval != 0) {
- if (retval == -ENOMEM)
- DBG(ep->dev, "Out of DMA memory\n");
- return retval;
- }
- if (ep->in) {
- if (req->req.length == ep->ep.maxpacket) {
- /* write tx bytes */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- ep->ep.maxpacket,
- UDC_DMA_IN_STS_TXBYTES);
-
- }
- }
-
- }
-
- if (ep->in) {
- VDBG(ep->dev, "IN: use_dma_ppb=%d req->req.len=%d "
- "maxpacket=%d ep%d\n",
- use_dma_ppb, req->req.length,
- ep->ep.maxpacket, ep->num);
- /*
- * if bytes < max packet then tx bytes must
- * be written in packet per buffer mode
- */
- if (!use_dma_ppb || req->req.length < ep->ep.maxpacket
- || ep->num == UDC_EP0OUT_IX
- || ep->num == UDC_EP0IN_IX) {
- /* write tx bytes */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- req->req.length,
- UDC_DMA_IN_STS_TXBYTES);
- /* reset frame num */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- 0,
- UDC_DMA_IN_STS_FRAMENUM);
- }
- /* set HOST BUSY */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- UDC_DMA_STP_STS_BS_HOST_BUSY,
- UDC_DMA_STP_STS_BS);
- } else {
- VDBG(ep->dev, "OUT set host ready\n");
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- UDC_DMA_STP_STS_BS_HOST_READY,
- UDC_DMA_STP_STS_BS);
-
-
- /* clear NAK by writing CNAK */
- if (ep->naking) {
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
-
- }
-
- return retval;
-}
-
-/* Completes request packet ... caller MUST hold lock */
-static void
-complete_req(struct udc_ep *ep, struct udc_request *req, int sts)
-__releases(ep->dev->lock)
-__acquires(ep->dev->lock)
-{
- struct udc *dev;
- unsigned halted;
-
- VDBG(ep->dev, "complete_req(): ep%d\n", ep->num);
-
- dev = ep->dev;
- /* unmap DMA */
- if (ep->dma)
- usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
-
- halted = ep->halted;
- ep->halted = 1;
-
- /* set new status if pending */
- if (req->req.status == -EINPROGRESS)
- req->req.status = sts;
-
- /* remove from ep queue */
- list_del_init(&req->queue);
-
- VDBG(ep->dev, "req %p => complete %d bytes at %s with sts %d\n",
- &req->req, req->req.length, ep->ep.name, sts);
-
- spin_unlock(&dev->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&dev->lock);
- ep->halted = halted;
-}
-
-/* Iterates to the end of a DMA chain and returns last descriptor */
-static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
-{
- struct udc_data_dma *td;
-
- td = req->td_data;
- while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L)))
- td = phys_to_virt(td->next);
-
- return td;
-
-}
-
-/* Iterates to the end of a DMA chain and counts bytes received */
-static u32 udc_get_ppbdu_rxbytes(struct udc_request *req)
-{
- struct udc_data_dma *td;
- u32 count;
-
- td = req->td_data;
- /* received number bytes */
- count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES);
-
- while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) {
- td = phys_to_virt(td->next);
- /* received number bytes */
- if (td) {
- count += AMD_GETBITS(td->status,
- UDC_DMA_OUT_STS_RXBYTES);
- }
- }
-
- return count;
-
-}
-
-/* Enabling RX DMA */
-static void udc_set_rde(struct udc *dev)
-{
- u32 tmp;
-
- VDBG(dev, "udc_set_rde()\n");
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* set RDE */
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_RDE);
- writel(tmp, &dev->regs->ctl);
-}
-
-/* Queues a request packet, called by gadget driver */
-static int
-udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
-{
- int retval = 0;
- u8 open_rxfifo = 0;
- unsigned long iflags;
- struct udc_ep *ep;
- struct udc_request *req;
- struct udc *dev;
- u32 tmp;
-
- /* check the inputs */
- req = container_of(usbreq, struct udc_request, req);
-
- if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf
- || !list_empty(&req->queue))
- return -EINVAL;
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
- return -EINVAL;
-
- VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in);
- dev = ep->dev;
-
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- /* map dma (usually done before) */
- if (ep->dma) {
- VDBG(dev, "DMA map req %p\n", req);
- retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in);
- if (retval)
- return retval;
- }
-
- VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n",
- usbep->name, usbreq, usbreq->length,
- req->td_data, usbreq->buf);
-
- spin_lock_irqsave(&dev->lock, iflags);
- usbreq->actual = 0;
- usbreq->status = -EINPROGRESS;
- req->dma_done = 0;
-
- /* on empty queue just do first transfer */
- if (list_empty(&ep->queue)) {
- /* zlp */
- if (usbreq->length == 0) {
- /* IN zlp's are handled by hardware */
- complete_req(ep, req, 0);
- VDBG(dev, "%s: zlp\n", ep->ep.name);
- /*
- * if set_config or set_intf is waiting for ack by zlp
- * then set CSR_DONE
- */
- if (dev->set_cfg_not_acked) {
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
- writel(tmp, &dev->regs->ctl);
- dev->set_cfg_not_acked = 0;
- }
- /* setup command is ACK'ed now by zlp */
- if (dev->waiting_zlp_ack_ep0in) {
- /* clear NAK by writing CNAK in EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX],
- UDC_EP0IN_IX);
- dev->waiting_zlp_ack_ep0in = 0;
- }
- goto finished;
- }
- if (ep->dma) {
- retval = prep_dma(ep, req, GFP_ATOMIC);
- if (retval != 0)
- goto finished;
- /* write desc pointer to enable DMA */
- if (ep->in) {
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(req->td_data->status,
- UDC_DMA_IN_STS_BS_HOST_READY,
- UDC_DMA_IN_STS_BS);
- }
-
- /* disabled rx dma while descriptor update */
- if (!ep->in) {
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* clear RDE */
- tmp = readl(&dev->regs->ctl);
- tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
- writel(tmp, &dev->regs->ctl);
- open_rxfifo = 1;
-
- /*
- * if BNA occurred then let BNA dummy desc.
- * point to current desc.
- */
- if (ep->bna_occurred) {
- VDBG(dev, "copy to BNA dummy desc.\n");
- memcpy(ep->bna_dummy_req->td_data,
- req->td_data,
- sizeof(struct udc_data_dma));
- }
- }
- /* write desc pointer */
- writel(req->td_phys, &ep->regs->desptr);
-
- /* clear NAK by writing CNAK */
- if (ep->naking) {
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->naking = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
-
- if (ep->in) {
- /* enable ep irq */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp &= AMD_UNMASK_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
- }
- } else if (ep->in) {
- /* enable ep irq */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp &= AMD_UNMASK_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
- }
-
- } else if (ep->dma) {
-
- /*
- * prep_dma not used for OUT ep's, this is not possible
- * for PPB modes, because of chain creation reasons
- */
- if (ep->in) {
- retval = prep_dma(ep, req, GFP_ATOMIC);
- if (retval != 0)
- goto finished;
- }
- }
- VDBG(dev, "list_add\n");
- /* add request to ep queue */
- if (req) {
-
- list_add_tail(&req->queue, &ep->queue);
-
- /* open rxfifo if out data queued */
- if (open_rxfifo) {
- /* enable DMA */
- req->dma_going = 1;
- udc_set_rde(dev);
- if (ep->num != UDC_EP0OUT_IX)
- dev->data_ep_queued = 1;
- }
- /* stop OUT naking */
- if (!ep->in) {
- if (!use_dma && udc_rxfifo_pending) {
- DBG(dev, "udc_queue(): pending bytes in "
- "rxfifo after nyet\n");
- /*
- * read pending bytes afer nyet:
- * referring to isr
- */
- if (udc_rxfifo_read(ep, req)) {
- /* finish */
- complete_req(ep, req, 0);
- }
- udc_rxfifo_pending = 0;
-
- }
- }
- }
-
-finished:
- spin_unlock_irqrestore(&dev->lock, iflags);
- return retval;
-}
-
-/* Empty request queue of an endpoint; caller holds spinlock */
-static void empty_req_queue(struct udc_ep *ep)
-{
- struct udc_request *req;
-
- ep->halted = 1;
- while (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct udc_request,
- queue);
- complete_req(ep, req, -ESHUTDOWN);
- }
-}
-
-/* Dequeues a request packet, called by gadget driver */
-static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
-{
- struct udc_ep *ep;
- struct udc_request *req;
- unsigned halted;
- unsigned long iflags;
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0
- && ep->num != UDC_EP0OUT_IX)))
- return -EINVAL;
-
- req = container_of(usbreq, struct udc_request, req);
-
- spin_lock_irqsave(&ep->dev->lock, iflags);
- halted = ep->halted;
- ep->halted = 1;
- /* request in processing or next one */
- if (ep->queue.next == &req->queue) {
- if (ep->dma && req->dma_going) {
- if (ep->in)
- ep->cancel_transfer = 1;
- else {
- u32 tmp;
- u32 dma_sts;
- /* stop potential receive DMA */
- tmp = readl(&udc->regs->ctl);
- writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_RDE),
- &udc->regs->ctl);
- /*
- * Cancel transfer later in ISR
- * if descriptor was touched.
- */
- dma_sts = AMD_GETBITS(req->td_data->status,
- UDC_DMA_OUT_STS_BS);
- if (dma_sts != UDC_DMA_OUT_STS_BS_HOST_READY)
- ep->cancel_transfer = 1;
- else {
- udc_init_bna_dummy(ep->req);
- writel(ep->bna_dummy_req->td_phys,
- &ep->regs->desptr);
- }
- writel(tmp, &udc->regs->ctl);
- }
- }
- }
- complete_req(ep, req, -ECONNRESET);
- ep->halted = halted;
-
- spin_unlock_irqrestore(&ep->dev->lock, iflags);
- return 0;
-}
-
-/* Halt or clear halt of endpoint */
-static int
-udc_set_halt(struct usb_ep *usbep, int halt)
-{
- struct udc_ep *ep;
- u32 tmp;
- unsigned long iflags;
- int retval = 0;
-
- if (!usbep)
- return -EINVAL;
-
- pr_debug("set_halt %s: halt=%d\n", usbep->name, halt);
-
- ep = container_of(usbep, struct udc_ep, ep);
- if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
- return -EINVAL;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- spin_lock_irqsave(&udc_stall_spinlock, iflags);
- /* halt or clear halt */
- if (halt) {
- if (ep->num == 0)
- ep->dev->stall_ep0in = 1;
- else {
- /*
- * set STALL
- * rxfifo empty not taken into acount
- */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- ep->halted = 1;
-
- /* setup poll timer */
- if (!timer_pending(&udc_pollstall_timer)) {
- udc_pollstall_timer.expires = jiffies +
- HZ * UDC_POLLSTALL_TIMER_USECONDS
- / (1000 * 1000);
- if (!stop_pollstall_timer) {
- DBG(ep->dev, "start polltimer\n");
- add_timer(&udc_pollstall_timer);
- }
- }
- }
- } else {
- /* ep is halted by set_halt() before */
- if (ep->halted) {
- tmp = readl(&ep->regs->ctl);
- /* clear stall bit */
- tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
- /* clear NAK by writing CNAK */
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->halted = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
- }
- spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
- return retval;
-}
-
-/* gadget interface */
-static const struct usb_ep_ops udc_ep_ops = {
- .enable = udc_ep_enable,
- .disable = udc_ep_disable,
-
- .alloc_request = udc_alloc_request,
- .free_request = udc_free_request,
-
- .queue = udc_queue,
- .dequeue = udc_dequeue,
-
- .set_halt = udc_set_halt,
- /* fifo ops not implemented */
-};
-
-/*-------------------------------------------------------------------------*/
-
-/* Get frame counter (not implemented) */
-static int udc_get_frame(struct usb_gadget *gadget)
-{
- return -EOPNOTSUPP;
-}
-
-/* Initiates a remote wakeup */
-static int udc_remote_wakeup(struct udc *dev)
-{
- unsigned long flags;
- u32 tmp;
-
- DBG(dev, "UDC initiates remote wakeup\n");
-
- spin_lock_irqsave(&dev->lock, flags);
-
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_RES);
- writel(tmp, &dev->regs->ctl);
- tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES);
- writel(tmp, &dev->regs->ctl);
-
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
-}
-
-/* Remote wakeup gadget interface */
-static int udc_wakeup(struct usb_gadget *gadget)
-{
- struct udc *dev;
-
- if (!gadget)
- return -EINVAL;
- dev = container_of(gadget, struct udc, gadget);
- udc_remote_wakeup(dev);
-
- return 0;
-}
-
-static int amd5536_udc_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
-static int amd5536_udc_stop(struct usb_gadget *g);
-
-static const struct usb_gadget_ops udc_ops = {
- .wakeup = udc_wakeup,
- .get_frame = udc_get_frame,
- .udc_start = amd5536_udc_start,
- .udc_stop = amd5536_udc_stop,
-};
-
-/* Setups endpoint parameters, adds endpoints to linked list */
-static void make_ep_lists(struct udc *dev)
-{
- /* make gadget ep lists */
- INIT_LIST_HEAD(&dev->gadget.ep_list);
- list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list,
- &dev->gadget.ep_list);
- list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list,
- &dev->gadget.ep_list);
- list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list,
- &dev->gadget.ep_list);
-
- /* fifo config */
- dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE;
- if (dev->gadget.speed == USB_SPEED_FULL)
- dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE;
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf;
- dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE;
-}
-
-/* Inits UDC context */
-static void udc_basic_init(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "udc_basic_init()\n");
-
- dev->gadget.speed = USB_SPEED_UNKNOWN;
-
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* stop poll stall timer */
- if (timer_pending(&udc_pollstall_timer))
- mod_timer(&udc_pollstall_timer, jiffies - 1);
- /* disable DMA */
- tmp = readl(&dev->regs->ctl);
- tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
- tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE);
- writel(tmp, &dev->regs->ctl);
-
- /* enable dynamic CSR programming */
- tmp = readl(&dev->regs->cfg);
- tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG);
- /* set self powered */
- tmp |= AMD_BIT(UDC_DEVCFG_SP);
- /* set remote wakeupable */
- tmp |= AMD_BIT(UDC_DEVCFG_RWKP);
- writel(tmp, &dev->regs->cfg);
-
- make_ep_lists(dev);
-
- dev->data_ep_enabled = 0;
- dev->data_ep_queued = 0;
-}
-
-/* init registers at driver load time */
-static int startup_registers(struct udc *dev)
-{
- u32 tmp;
-
- /* init controller by soft reset */
- udc_soft_reset(dev);
-
- /* mask not needed interrupts */
- udc_mask_unused_interrupts(dev);
-
- /* put into initial config */
- udc_basic_init(dev);
- /* link up all endpoints */
- udc_setup_endpoints(dev);
-
- /* program speed */
- tmp = readl(&dev->regs->cfg);
- if (use_fullspeed)
- tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
- else
- tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
- writel(tmp, &dev->regs->cfg);
-
- return 0;
-}
-
-/* Sets initial endpoint parameters */
-static void udc_setup_endpoints(struct udc *dev)
-{
- struct udc_ep *ep;
- u32 tmp;
- u32 reg;
-
- DBG(dev, "udc_setup_endpoints()\n");
-
- /* read enum speed */
- tmp = readl(&dev->regs->sts);
- tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
- if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
- dev->gadget.speed = USB_SPEED_HIGH;
- else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
- dev->gadget.speed = USB_SPEED_FULL;
-
- /* set basic ep parameters */
- for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
- ep = &dev->ep[tmp];
- ep->dev = dev;
- ep->ep.name = ep_info[tmp].name;
- ep->ep.caps = ep_info[tmp].caps;
- ep->num = tmp;
- /* txfifo size is calculated at enable time */
- ep->txfifo = dev->txfifo;
-
- /* fifo size */
- if (tmp < UDC_EPIN_NUM) {
- ep->fifo_depth = UDC_TXFIFO_SIZE;
- ep->in = 1;
- } else {
- ep->fifo_depth = UDC_RXFIFO_SIZE;
- ep->in = 0;
-
- }
- ep->regs = &dev->ep_regs[tmp];
- /*
- * ep will be reset only if ep was not enabled before to avoid
- * disabling ep interrupts when ENUM interrupt occurs but ep is
- * not enabled by gadget driver
- */
- if (!ep->ep.desc)
- ep_init(dev->regs, ep);
-
- if (use_dma) {
- /*
- * ep->dma is not really used, just to indicate that
- * DMA is active: remove this
- * dma regs = dev control regs
- */
- ep->dma = &dev->regs->ctl;
-
- /* nak OUT endpoints until enable - not for ep0 */
- if (tmp != UDC_EP0IN_IX && tmp != UDC_EP0OUT_IX
- && tmp > UDC_EPIN_NUM) {
- /* set NAK */
- reg = readl(&dev->ep[tmp].regs->ctl);
- reg |= AMD_BIT(UDC_EPCTL_SNAK);
- writel(reg, &dev->ep[tmp].regs->ctl);
- dev->ep[tmp].naking = 1;
-
- }
- }
- }
- /* EP0 max packet */
- if (dev->gadget.speed == USB_SPEED_FULL) {
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
- UDC_FS_EP0IN_MAX_PKT_SIZE);
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
- UDC_FS_EP0OUT_MAX_PKT_SIZE);
- } else if (dev->gadget.speed == USB_SPEED_HIGH) {
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
- UDC_EP0IN_MAX_PKT_SIZE);
- usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
- UDC_EP0OUT_MAX_PKT_SIZE);
- }
-
- /*
- * with suspend bug workaround, ep0 params for gadget driver
- * are set at gadget driver bind() call
- */
- dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
- dev->ep[UDC_EP0IN_IX].halted = 0;
- INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
-
- /* init cfg/alt/int */
- dev->cur_config = 0;
- dev->cur_intf = 0;
- dev->cur_alt = 0;
-}
-
-/* Bringup after Connect event, initial bringup to be ready for ep0 events */
-static void usb_connect(struct udc *dev)
-{
-
- dev_info(&dev->pdev->dev, "USB Connect\n");
-
- dev->connected = 1;
-
- /* put into initial config */
- udc_basic_init(dev);
-
- /* enable device setup interrupts */
- udc_enable_dev_setup_interrupts(dev);
-}
-
-/*
- * Calls gadget with disconnect event and resets the UDC and makes
- * initial bringup to be ready for ep0 events
- */
-static void usb_disconnect(struct udc *dev)
-{
-
- dev_info(&dev->pdev->dev, "USB Disconnect\n");
-
- dev->connected = 0;
-
- /* mask interrupts */
- udc_mask_unused_interrupts(dev);
-
- /* REVISIT there doesn't seem to be a point to having this
- * talk to a tasklet ... do it directly, we already hold
- * the spinlock needed to process the disconnect.
- */
-
- tasklet_schedule(&disconnect_tasklet);
-}
-
-/* Tasklet for disconnect to be outside of interrupt context */
-static void udc_tasklet_disconnect(unsigned long par)
-{
- struct udc *dev = (struct udc *)(*((struct udc **) par));
- u32 tmp;
-
- DBG(dev, "Tasklet disconnect\n");
- spin_lock_irq(&dev->lock);
-
- if (dev->driver) {
- spin_unlock(&dev->lock);
- dev->driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
-
- /* empty queues */
- for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
- empty_req_queue(&dev->ep[tmp]);
-
- }
-
- /* disable ep0 */
- ep_init(dev->regs,
- &dev->ep[UDC_EP0IN_IX]);
-
-
- if (!soft_reset_occured) {
- /* init controller by soft reset */
- udc_soft_reset(dev);
- soft_reset_occured++;
- }
-
- /* re-enable dev interrupts */
- udc_enable_dev_setup_interrupts(dev);
- /* back to full speed ? */
- if (use_fullspeed) {
- tmp = readl(&dev->regs->cfg);
- tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
- writel(tmp, &dev->regs->cfg);
- }
-
- spin_unlock_irq(&dev->lock);
-}
-
-/* Reset the UDC core */
-static void udc_soft_reset(struct udc *dev)
-{
- unsigned long flags;
-
- DBG(dev, "Soft reset\n");
- /*
- * reset possible waiting interrupts, because int.
- * status is lost after soft reset,
- * ep int. status reset
- */
- writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
- /* device int. status reset */
- writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
-
- spin_lock_irqsave(&udc_irq_spinlock, flags);
- writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
- readl(&dev->regs->cfg);
- spin_unlock_irqrestore(&udc_irq_spinlock, flags);
-
-}
-
-/* RDE timer callback to set RDE bit */
-static void udc_timer_function(unsigned long v)
-{
- u32 tmp;
-
- spin_lock_irq(&udc_irq_spinlock);
-
- if (set_rde > 0) {
- /*
- * open the fifo if fifo was filled on last timer call
- * conditionally
- */
- if (set_rde > 1) {
- /* set RDE to receive setup data */
- tmp = readl(&udc->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_RDE);
- writel(tmp, &udc->regs->ctl);
- set_rde = -1;
- } else if (readl(&udc->regs->sts)
- & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
- /*
- * if fifo empty setup polling, do not just
- * open the fifo
- */
- udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV;
- if (!stop_timer)
- add_timer(&udc_timer);
- } else {
- /*
- * fifo contains data now, setup timer for opening
- * the fifo when timer expires to be able to receive
- * setup packets, when data packets gets queued by
- * gadget layer then timer will forced to expire with
- * set_rde=0 (RDE is set in udc_queue())
- */
- set_rde++;
- /* debug: lhadmot_timer_start = 221070 */
- udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
-
- } else
- set_rde = -1; /* RDE was set by udc_queue() */
- spin_unlock_irq(&udc_irq_spinlock);
- if (stop_timer)
- complete(&on_exit);
-
-}
-
-/* Handle halt state, used in stall poll timer */
-static void udc_handle_halt_state(struct udc_ep *ep)
-{
- u32 tmp;
- /* set stall as long not halted */
- if (ep->halted == 1) {
- tmp = readl(&ep->regs->ctl);
- /* STALL cleared ? */
- if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
- /*
- * FIXME: MSC spec requires that stall remains
- * even on receivng of CLEAR_FEATURE HALT. So
- * we would set STALL again here to be compliant.
- * But with current mass storage drivers this does
- * not work (would produce endless host retries).
- * So we clear halt on CLEAR_FEATURE.
- *
- DBG(ep->dev, "ep %d: set STALL again\n", ep->num);
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);*/
-
- /* clear NAK by writing CNAK */
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &ep->regs->ctl);
- ep->halted = 0;
- UDC_QUEUE_CNAK(ep, ep->num);
- }
- }
-}
-
-/* Stall timer callback to poll S bit and set it again after */
-static void udc_pollstall_timer_function(unsigned long v)
-{
- struct udc_ep *ep;
- int halted = 0;
-
- spin_lock_irq(&udc_stall_spinlock);
- /*
- * only one IN and OUT endpoints are handled
- * IN poll stall
- */
- ep = &udc->ep[UDC_EPIN_IX];
- udc_handle_halt_state(ep);
- if (ep->halted)
- halted = 1;
- /* OUT poll stall */
- ep = &udc->ep[UDC_EPOUT_IX];
- udc_handle_halt_state(ep);
- if (ep->halted)
- halted = 1;
-
- /* setup timer again when still halted */
- if (!stop_pollstall_timer && halted) {
- udc_pollstall_timer.expires = jiffies +
- HZ * UDC_POLLSTALL_TIMER_USECONDS
- / (1000 * 1000);
- add_timer(&udc_pollstall_timer);
- }
- spin_unlock_irq(&udc_stall_spinlock);
-
- if (stop_pollstall_timer)
- complete(&on_pollstall_exit);
-}
-
-/* Inits endpoint 0 so that SETUP packets are processed */
-static void activate_control_endpoints(struct udc *dev)
-{
- u32 tmp;
-
- DBG(dev, "activate_control_endpoints\n");
-
- /* flush fifo */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_F);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
-
- /* set ep0 directions */
- dev->ep[UDC_EP0IN_IX].in = 1;
- dev->ep[UDC_EP0OUT_IX].in = 0;
-
- /* set buffer size (tx fifo entries) of EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE,
- UDC_EPIN_BUFF_SIZE);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE,
- UDC_EPIN_BUFF_SIZE);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
-
- /* set max packet size of EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
-
- /* set max packet size of EP0_OUT */
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
- UDC_EP_MAX_PKT_SIZE);
- writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
-
- /* set max packet size of EP0 in UDC CSR */
- tmp = readl(&dev->csr->ne[0]);
- if (dev->gadget.speed == USB_SPEED_FULL)
- tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
- UDC_CSR_NE_MAX_PKT);
- else if (dev->gadget.speed == USB_SPEED_HIGH)
- tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
- UDC_CSR_NE_MAX_PKT);
- writel(tmp, &dev->csr->ne[0]);
-
- if (use_dma) {
- dev->ep[UDC_EP0OUT_IX].td->status |=
- AMD_BIT(UDC_DMA_OUT_STS_L);
- /* write dma desc address */
- writel(dev->ep[UDC_EP0OUT_IX].td_stp_dma,
- &dev->ep[UDC_EP0OUT_IX].regs->subptr);
- writel(dev->ep[UDC_EP0OUT_IX].td_phys,
- &dev->ep[UDC_EP0OUT_IX].regs->desptr);
- /* stop RDE timer */
- if (timer_pending(&udc_timer)) {
- set_rde = 0;
- mod_timer(&udc_timer, jiffies - 1);
- }
- /* stop pollstall timer */
- if (timer_pending(&udc_pollstall_timer))
- mod_timer(&udc_pollstall_timer, jiffies - 1);
- /* enable DMA */
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_MODE)
- | AMD_BIT(UDC_DEVCTL_RDE)
- | AMD_BIT(UDC_DEVCTL_TDE);
- if (use_dma_bufferfill_mode)
- tmp |= AMD_BIT(UDC_DEVCTL_BF);
- else if (use_dma_ppb_du)
- tmp |= AMD_BIT(UDC_DEVCTL_DU);
- writel(tmp, &dev->regs->ctl);
- }
-
- /* clear NAK by writing CNAK for EP0IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
-
- /* clear NAK by writing CNAK for EP0OUT */
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
- dev->ep[UDC_EP0OUT_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
-}
-
-/* Make endpoint 0 ready for control traffic */
-static int setup_ep0(struct udc *dev)
-{
- activate_control_endpoints(dev);
- /* enable ep0 interrupts */
- udc_enable_ep0_interrupts(dev);
- /* enable device setup interrupts */
- udc_enable_dev_setup_interrupts(dev);
-
- return 0;
-}
-
-/* Called by gadget driver to register itself */
-static int amd5536_udc_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
-{
- struct udc *dev = to_amd5536_udc(g);
- u32 tmp;
-
- driver->driver.bus = NULL;
- dev->driver = driver;
-
- /* Some gadget drivers use both ep0 directions.
- * NOTE: to gadget driver, ep0 is just one endpoint...
- */
- dev->ep[UDC_EP0OUT_IX].ep.driver_data =
- dev->ep[UDC_EP0IN_IX].ep.driver_data;
-
- /* get ready for ep0 traffic */
- setup_ep0(dev);
-
- /* clear SD */
- tmp = readl(&dev->regs->ctl);
- tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD);
- writel(tmp, &dev->regs->ctl);
-
- usb_connect(dev);
-
- return 0;
-}
-
-/* shutdown requests and disconnect from gadget */
-static void
-shutdown(struct udc *dev, struct usb_gadget_driver *driver)
-__releases(dev->lock)
-__acquires(dev->lock)
-{
- int tmp;
-
- /* empty queues and init hardware */
- udc_basic_init(dev);
-
- for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
- empty_req_queue(&dev->ep[tmp]);
-
- udc_setup_endpoints(dev);
-}
-
-/* Called by gadget driver to unregister itself */
-static int amd5536_udc_stop(struct usb_gadget *g)
-{
- struct udc *dev = to_amd5536_udc(g);
- unsigned long flags;
- u32 tmp;
-
- spin_lock_irqsave(&dev->lock, flags);
- udc_mask_unused_interrupts(dev);
- shutdown(dev, NULL);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev->driver = NULL;
-
- /* set SD */
- tmp = readl(&dev->regs->ctl);
- tmp |= AMD_BIT(UDC_DEVCTL_SD);
- writel(tmp, &dev->regs->ctl);
-
- return 0;
-}
-
-/* Clear pending NAK bits */
-static void udc_process_cnak_queue(struct udc *dev)
-{
- u32 tmp;
- u32 reg;
-
- /* check epin's */
- DBG(dev, "CNAK pending queue processing\n");
- for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) {
- if (cnak_pending & (1 << tmp)) {
- DBG(dev, "CNAK pending for ep%d\n", tmp);
- /* clear NAK by writing CNAK */
- reg = readl(&dev->ep[tmp].regs->ctl);
- reg |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(reg, &dev->ep[tmp].regs->ctl);
- dev->ep[tmp].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num);
- }
- }
- /* ... and ep0out */
- if (cnak_pending & (1 << UDC_EP0OUT_IX)) {
- DBG(dev, "CNAK pending for ep%d\n", UDC_EP0OUT_IX);
- /* clear NAK by writing CNAK */
- reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
- reg |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
- dev->ep[UDC_EP0OUT_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX],
- dev->ep[UDC_EP0OUT_IX].num);
- }
-}
-
-/* Enabling RX DMA after setup packet */
-static void udc_ep0_set_rde(struct udc *dev)
-{
- if (use_dma) {
- /*
- * only enable RXDMA when no data endpoint enabled
- * or data is queued
- */
- if (!dev->data_ep_enabled || dev->data_ep_queued) {
- udc_set_rde(dev);
- } else {
- /*
- * setup timer for enabling RDE (to not enable
- * RXFIFO DMA for data endpoints to early)
- */
- if (set_rde != 0 && !timer_pending(&udc_timer)) {
- udc_timer.expires =
- jiffies + HZ/UDC_RDE_TIMER_DIV;
- set_rde = 1;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
- }
- }
-}
-
-
-/* Interrupt handler for data OUT traffic */
-static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- struct udc_ep *ep;
- struct udc_request *req;
- unsigned int count;
- struct udc_data_dma *td = NULL;
- unsigned dma_done;
-
- VDBG(dev, "ep%d irq\n", ep_ix);
- ep = &dev->ep[ep_ix];
-
- tmp = readl(&ep->regs->sts);
- if (use_dma) {
- /* BNA event ? */
- if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
- DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n",
- ep->num, readl(&ep->regs->desptr));
- /* clear BNA */
- writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts);
- if (!ep->cancel_transfer)
- ep->bna_occurred = 1;
- else
- ep->cancel_transfer = 0;
- ret_val = IRQ_HANDLED;
- goto finished;
- }
- }
- /* HE event ? */
- if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
-
- /* clear HE */
- writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
- ret_val = IRQ_HANDLED;
- goto finished;
- }
-
- if (!list_empty(&ep->queue)) {
-
- /* next request */
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- } else {
- req = NULL;
- udc_rxfifo_pending = 1;
- }
- VDBG(dev, "req = %p\n", req);
- /* fifo mode */
- if (!use_dma) {
-
- /* read fifo */
- if (req && udc_rxfifo_read(ep, req)) {
- ret_val = IRQ_HANDLED;
-
- /* finish */
- complete_req(ep, req, 0);
- /* next request */
- if (!list_empty(&ep->queue) && !ep->halted) {
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- } else
- req = NULL;
- }
-
- /* DMA */
- } else if (!ep->cancel_transfer && req) {
- ret_val = IRQ_HANDLED;
-
- /* check for DMA done */
- if (!use_dma_ppb) {
- dma_done = AMD_GETBITS(req->td_data->status,
- UDC_DMA_OUT_STS_BS);
- /* packet per buffer mode - rx bytes */
- } else {
- /*
- * if BNA occurred then recover desc. from
- * BNA dummy desc.
- */
- if (ep->bna_occurred) {
- VDBG(dev, "Recover desc. from BNA dummy\n");
- memcpy(req->td_data, ep->bna_dummy_req->td_data,
- sizeof(struct udc_data_dma));
- ep->bna_occurred = 0;
- udc_init_bna_dummy(ep->req);
- }
- td = udc_get_last_dma_desc(req);
- dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS);
- }
- if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
- /* buffer fill mode - rx bytes */
- if (!use_dma_ppb) {
- /* received number bytes */
- count = AMD_GETBITS(req->td_data->status,
- UDC_DMA_OUT_STS_RXBYTES);
- VDBG(dev, "rx bytes=%u\n", count);
- /* packet per buffer mode - rx bytes */
- } else {
- VDBG(dev, "req->td_data=%p\n", req->td_data);
- VDBG(dev, "last desc = %p\n", td);
- /* received number bytes */
- if (use_dma_ppb_du) {
- /* every desc. counts bytes */
- count = udc_get_ppbdu_rxbytes(req);
- } else {
- /* last desc. counts bytes */
- count = AMD_GETBITS(td->status,
- UDC_DMA_OUT_STS_RXBYTES);
- if (!count && req->req.length
- == UDC_DMA_MAXPACKET) {
- /*
- * on 64k packets the RXBYTES
- * field is zero
- */
- count = UDC_DMA_MAXPACKET;
- }
- }
- VDBG(dev, "last desc rx bytes=%u\n", count);
- }
-
- tmp = req->req.length - req->req.actual;
- if (count > tmp) {
- if ((tmp % ep->ep.maxpacket) != 0) {
- DBG(dev, "%s: rx %db, space=%db\n",
- ep->ep.name, count, tmp);
- req->req.status = -EOVERFLOW;
- }
- count = tmp;
- }
- req->req.actual += count;
- req->dma_going = 0;
- /* complete request */
- complete_req(ep, req, 0);
-
- /* next request */
- if (!list_empty(&ep->queue) && !ep->halted) {
- req = list_entry(ep->queue.next,
- struct udc_request,
- queue);
- /*
- * DMA may be already started by udc_queue()
- * called by gadget drivers completion
- * routine. This happens when queue
- * holds one request only.
- */
- if (req->dma_going == 0) {
- /* next dma */
- if (prep_dma(ep, req, GFP_ATOMIC) != 0)
- goto finished;
- /* write desc pointer */
- writel(req->td_phys,
- &ep->regs->desptr);
- req->dma_going = 1;
- /* enable DMA */
- udc_set_rde(dev);
- }
- } else {
- /*
- * implant BNA dummy descriptor to allow
- * RXFIFO opening by RDE
- */
- if (ep->bna_dummy_req) {
- /* write desc pointer */
- writel(ep->bna_dummy_req->td_phys,
- &ep->regs->desptr);
- ep->bna_occurred = 0;
- }
-
- /*
- * schedule timer for setting RDE if queue
- * remains empty to allow ep0 packets pass
- * through
- */
- if (set_rde != 0
- && !timer_pending(&udc_timer)) {
- udc_timer.expires =
- jiffies
- + HZ*UDC_RDE_TIMER_SECONDS;
- set_rde = 1;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
- if (ep->num != UDC_EP0OUT_IX)
- dev->data_ep_queued = 0;
- }
-
- } else {
- /*
- * RX DMA must be reenabled for each desc in PPBDU mode
- * and must be enabled for PPBNDU mode in case of BNA
- */
- udc_set_rde(dev);
- }
-
- } else if (ep->cancel_transfer) {
- ret_val = IRQ_HANDLED;
- ep->cancel_transfer = 0;
- }
-
- /* check pending CNAKS */
- if (cnak_pending) {
- /* CNAk processing when rxfifo empty only */
- if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
- udc_process_cnak_queue(dev);
- }
-
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR, &ep->regs->sts);
-finished:
- return ret_val;
-}
-
-/* Interrupt handler for data IN traffic */
-static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- u32 epsts;
- struct udc_ep *ep;
- struct udc_request *req;
- struct udc_data_dma *td;
- unsigned len;
-
- ep = &dev->ep[ep_ix];
-
- epsts = readl(&ep->regs->sts);
- if (use_dma) {
- /* BNA ? */
- if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
- dev_err(&dev->pdev->dev,
- "BNA ep%din occurred - DESPTR = %08lx\n",
- ep->num,
- (unsigned long) readl(&ep->regs->desptr));
-
- /* clear BNA */
- writel(epsts, &ep->regs->sts);
- ret_val = IRQ_HANDLED;
- goto finished;
- }
- }
- /* HE event ? */
- if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev,
- "HE ep%dn occurred - DESPTR = %08lx\n",
- ep->num, (unsigned long) readl(&ep->regs->desptr));
-
- /* clear HE */
- writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
- ret_val = IRQ_HANDLED;
- goto finished;
- }
-
- /* DMA completion */
- if (epsts & AMD_BIT(UDC_EPSTS_TDC)) {
- VDBG(dev, "TDC set- completion\n");
- ret_val = IRQ_HANDLED;
- if (!ep->cancel_transfer && !list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- /*
- * length bytes transferred
- * check dma done of last desc. in PPBDU mode
- */
- if (use_dma_ppb_du) {
- td = udc_get_last_dma_desc(req);
- if (td)
- req->req.actual = req->req.length;
- } else {
- /* assume all bytes transferred */
- req->req.actual = req->req.length;
- }
-
- if (req->req.actual == req->req.length) {
- /* complete req */
- complete_req(ep, req, 0);
- req->dma_going = 0;
- /* further request available ? */
- if (list_empty(&ep->queue)) {
- /* disable interrupt */
- tmp = readl(&dev->regs->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp, &dev->regs->ep_irqmsk);
- }
- }
- }
- ep->cancel_transfer = 0;
-
- }
- /*
- * status reg has IN bit set and TDC not set (if TDC was handled,
- * IN must not be handled (UDC defect) ?
- */
- if ((epsts & AMD_BIT(UDC_EPSTS_IN))
- && !(epsts & AMD_BIT(UDC_EPSTS_TDC))) {
- ret_val = IRQ_HANDLED;
- if (!list_empty(&ep->queue)) {
- /* next request */
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
- /* FIFO mode */
- if (!use_dma) {
- /* write fifo */
- udc_txfifo_write(ep, &req->req);
- len = req->req.length - req->req.actual;
- if (len > ep->ep.maxpacket)
- len = ep->ep.maxpacket;
- req->req.actual += len;
- if (req->req.actual == req->req.length
- || (len != ep->ep.maxpacket)) {
- /* complete req */
- complete_req(ep, req, 0);
- }
- /* DMA */
- } else if (req && !req->dma_going) {
- VDBG(dev, "IN DMA : req=%p req->td_data=%p\n",
- req, req->td_data);
- if (req->td_data) {
-
- req->dma_going = 1;
-
- /*
- * unset L bit of first desc.
- * for chain
- */
- if (use_dma_ppb && req->req.length >
- ep->ep.maxpacket) {
- req->td_data->status &=
- AMD_CLEAR_BIT(
- UDC_DMA_IN_STS_L);
- }
-
- /* write desc pointer */
- writel(req->td_phys, &ep->regs->desptr);
-
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(
- req->td_data->status,
- UDC_DMA_IN_STS_BS_HOST_READY,
- UDC_DMA_IN_STS_BS);
-
- /* set poll demand bit */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_P);
- writel(tmp, &ep->regs->ctl);
- }
- }
-
- } else if (!use_dma && ep->in) {
- /* disable interrupt */
- tmp = readl(
- &dev->regs->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp,
- &dev->regs->ep_irqmsk);
- }
- }
- /* clear status bits */
- writel(epsts, &ep->regs->sts);
-
-finished:
- return ret_val;
-
-}
-
-/* Interrupt handler for Control OUT traffic */
-static irqreturn_t udc_control_out_isr(struct udc *dev)
-__releases(dev->lock)
-__acquires(dev->lock)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- int setup_supported;
- u32 count;
- int set = 0;
- struct udc_ep *ep;
- struct udc_ep *ep_tmp;
-
- ep = &dev->ep[UDC_EP0OUT_IX];
-
- /* clear irq */
- writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts);
-
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
- /* check BNA and clear if set */
- if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
- VDBG(dev, "ep0: BNA set\n");
- writel(AMD_BIT(UDC_EPSTS_BNA),
- &dev->ep[UDC_EP0OUT_IX].regs->sts);
- ep->bna_occurred = 1;
- ret_val = IRQ_HANDLED;
- goto finished;
- }
-
- /* type of data: SETUP or DATA 0 bytes */
- tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT);
- VDBG(dev, "data_typ = %x\n", tmp);
-
- /* setup data */
- if (tmp == UDC_EPSTS_OUT_SETUP) {
- ret_val = IRQ_HANDLED;
-
- ep->dev->stall_ep0in = 0;
- dev->waiting_zlp_ack_ep0in = 0;
-
- /* set NAK for EP0_IN */
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_SNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 1;
- /* get setup data */
- if (use_dma) {
-
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR,
- &dev->ep[UDC_EP0OUT_IX].regs->sts);
-
- setup_data.data[0] =
- dev->ep[UDC_EP0OUT_IX].td_stp->data12;
- setup_data.data[1] =
- dev->ep[UDC_EP0OUT_IX].td_stp->data34;
- /* set HOST READY */
- dev->ep[UDC_EP0OUT_IX].td_stp->status =
- UDC_DMA_STP_STS_BS_HOST_READY;
- } else {
- /* read fifo */
- udc_rxfifo_read_dwords(dev, setup_data.data, 2);
- }
-
- /* determine direction of control data */
- if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) {
- dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
- /* enable RDE */
- udc_ep0_set_rde(dev);
- set = 0;
- } else {
- dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep;
- /*
- * implant BNA dummy descriptor to allow RXFIFO opening
- * by RDE
- */
- if (ep->bna_dummy_req) {
- /* write desc pointer */
- writel(ep->bna_dummy_req->td_phys,
- &dev->ep[UDC_EP0OUT_IX].regs->desptr);
- ep->bna_occurred = 0;
- }
-
- set = 1;
- dev->ep[UDC_EP0OUT_IX].naking = 1;
- /*
- * setup timer for enabling RDE (to not enable
- * RXFIFO DMA for data to early)
- */
- set_rde = 1;
- if (!timer_pending(&udc_timer)) {
- udc_timer.expires = jiffies +
- HZ/UDC_RDE_TIMER_DIV;
- if (!stop_timer)
- add_timer(&udc_timer);
- }
- }
-
- /*
- * mass storage reset must be processed here because
- * next packet may be a CLEAR_FEATURE HALT which would not
- * clear the stall bit when no STALL handshake was received
- * before (autostall can cause this)
- */
- if (setup_data.data[0] == UDC_MSCRES_DWORD0
- && setup_data.data[1] == UDC_MSCRES_DWORD1) {
- DBG(dev, "MSC Reset\n");
- /*
- * clear stall bits
- * only one IN and OUT endpoints are handled
- */
- ep_tmp = &udc->ep[UDC_EPIN_IX];
- udc_set_halt(&ep_tmp->ep, 0);
- ep_tmp = &udc->ep[UDC_EPOUT_IX];
- udc_set_halt(&ep_tmp->ep, 0);
- }
-
- /* call gadget with setup data received */
- spin_unlock(&dev->lock);
- setup_supported = dev->driver->setup(&dev->gadget,
- &setup_data.request);
- spin_lock(&dev->lock);
-
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- /* ep0 in returns data (not zlp) on IN phase */
- if (setup_supported >= 0 && setup_supported <
- UDC_EP0IN_MAXPACKET) {
- /* clear NAK by writing CNAK in EP0_IN */
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- dev->ep[UDC_EP0IN_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
-
- /* if unsupported request then stall */
- } else if (setup_supported < 0) {
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
- } else
- dev->waiting_zlp_ack_ep0in = 1;
-
-
- /* clear NAK by writing CNAK in EP0_OUT */
- if (!set) {
- tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_CNAK);
- writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
- dev->ep[UDC_EP0OUT_IX].naking = 0;
- UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
- }
-
- if (!use_dma) {
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR,
- &dev->ep[UDC_EP0OUT_IX].regs->sts);
- }
-
- /* data packet 0 bytes */
- } else if (tmp == UDC_EPSTS_OUT_DATA) {
- /* clear OUT bits in ep status */
- writel(UDC_EPSTS_OUT_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
-
- /* get setup data: only 0 packet */
- if (use_dma) {
- /* no req if 0 packet, just reactivate */
- if (list_empty(&dev->ep[UDC_EP0OUT_IX].queue)) {
- VDBG(dev, "ZLP\n");
-
- /* set HOST READY */
- dev->ep[UDC_EP0OUT_IX].td->status =
- AMD_ADDBITS(
- dev->ep[UDC_EP0OUT_IX].td->status,
- UDC_DMA_OUT_STS_BS_HOST_READY,
- UDC_DMA_OUT_STS_BS);
- /* enable RDE */
- udc_ep0_set_rde(dev);
- ret_val = IRQ_HANDLED;
-
- } else {
- /* control write */
- ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
- /* re-program desc. pointer for possible ZLPs */
- writel(dev->ep[UDC_EP0OUT_IX].td_phys,
- &dev->ep[UDC_EP0OUT_IX].regs->desptr);
- /* enable RDE */
- udc_ep0_set_rde(dev);
- }
- } else {
-
- /* received number bytes */
- count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
- count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE);
- /* out data for fifo mode not working */
- count = 0;
-
- /* 0 packet or real data ? */
- if (count != 0) {
- ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
- } else {
- /* dummy read confirm */
- readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm);
- ret_val = IRQ_HANDLED;
- }
- }
- }
-
- /* check pending CNAKS */
- if (cnak_pending) {
- /* CNAk processing when rxfifo empty only */
- if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
- udc_process_cnak_queue(dev);
- }
-
-finished:
- return ret_val;
-}
-
-/* Interrupt handler for Control IN traffic */
-static irqreturn_t udc_control_in_isr(struct udc *dev)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- struct udc_ep *ep;
- struct udc_request *req;
- unsigned len;
-
- ep = &dev->ep[UDC_EP0IN_IX];
-
- /* clear irq */
- writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts);
-
- tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
- /* DMA completion */
- if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
- VDBG(dev, "isr: TDC clear\n");
- ret_val = IRQ_HANDLED;
-
- /* clear TDC bit */
- writel(AMD_BIT(UDC_EPSTS_TDC),
- &dev->ep[UDC_EP0IN_IX].regs->sts);
-
- /* status reg has IN bit set ? */
- } else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
- ret_val = IRQ_HANDLED;
-
- if (ep->dma) {
- /* clear IN bit */
- writel(AMD_BIT(UDC_EPSTS_IN),
- &dev->ep[UDC_EP0IN_IX].regs->sts);
- }
- if (dev->stall_ep0in) {
- DBG(dev, "stall ep0in\n");
- /* halt ep0in */
- tmp = readl(&ep->regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- } else {
- if (!list_empty(&ep->queue)) {
- /* next request */
- req = list_entry(ep->queue.next,
- struct udc_request, queue);
-
- if (ep->dma) {
- /* write desc pointer */
- writel(req->td_phys, &ep->regs->desptr);
- /* set HOST READY */
- req->td_data->status =
- AMD_ADDBITS(
- req->td_data->status,
- UDC_DMA_STP_STS_BS_HOST_READY,
- UDC_DMA_STP_STS_BS);
-
- /* set poll demand bit */
- tmp =
- readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
- tmp |= AMD_BIT(UDC_EPCTL_P);
- writel(tmp,
- &dev->ep[UDC_EP0IN_IX].regs->ctl);
-
- /* all bytes will be transferred */
- req->req.actual = req->req.length;
-
- /* complete req */
- complete_req(ep, req, 0);
-
- } else {
- /* write fifo */
- udc_txfifo_write(ep, &req->req);
- /* lengh bytes transferred */
- len = req->req.length - req->req.actual;
- if (len > ep->ep.maxpacket)
- len = ep->ep.maxpacket;
-
- req->req.actual += len;
- if (req->req.actual == req->req.length
- || (len != ep->ep.maxpacket)) {
- /* complete req */
- complete_req(ep, req, 0);
- }
- }
-
- }
- }
- ep->halted = 0;
- dev->stall_ep0in = 0;
- if (!ep->dma) {
- /* clear IN bit */
- writel(AMD_BIT(UDC_EPSTS_IN),
- &dev->ep[UDC_EP0IN_IX].regs->sts);
- }
- }
-
- return ret_val;
-}
-
-
-/* Interrupt handler for global device events */
-static irqreturn_t udc_dev_isr(struct udc *dev, u32 dev_irq)
-__releases(dev->lock)
-__acquires(dev->lock)
-{
- irqreturn_t ret_val = IRQ_NONE;
- u32 tmp;
- u32 cfg;
- struct udc_ep *ep;
- u16 i;
- u8 udc_csr_epix;
-
- /* SET_CONFIG irq ? */
- if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) {
- ret_val = IRQ_HANDLED;
-
- /* read config value */
- tmp = readl(&dev->regs->sts);
- cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG);
- DBG(dev, "SET_CONFIG interrupt: config=%d\n", cfg);
- dev->cur_config = cfg;
- dev->set_cfg_not_acked = 1;
-
- /* make usb request for gadget driver */
- memset(&setup_data, 0 , sizeof(union udc_setup_data));
- setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
- setup_data.request.wValue = cpu_to_le16(dev->cur_config);
-
- /* programm the NE registers */
- for (i = 0; i < UDC_EP_NUM; i++) {
- ep = &dev->ep[i];
- if (ep->in) {
-
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num;
-
-
- /* OUT ep */
- } else {
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
- }
-
- tmp = readl(&dev->csr->ne[udc_csr_epix]);
- /* ep cfg */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_config,
- UDC_CSR_NE_CFG);
- /* write reg */
- writel(tmp, &dev->csr->ne[udc_csr_epix]);
-
- /* clear stall bits */
- ep->halted = 0;
- tmp = readl(&ep->regs->ctl);
- tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- }
- /* call gadget zero with setup data received */
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
- spin_lock(&dev->lock);
-
- } /* SET_INTERFACE ? */
- if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) {
- ret_val = IRQ_HANDLED;
-
- dev->set_cfg_not_acked = 1;
- /* read interface and alt setting values */
- tmp = readl(&dev->regs->sts);
- dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT);
- dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF);
-
- /* make usb request for gadget driver */
- memset(&setup_data, 0 , sizeof(union udc_setup_data));
- setup_data.request.bRequest = USB_REQ_SET_INTERFACE;
- setup_data.request.bRequestType = USB_RECIP_INTERFACE;
- setup_data.request.wValue = cpu_to_le16(dev->cur_alt);
- setup_data.request.wIndex = cpu_to_le16(dev->cur_intf);
-
- DBG(dev, "SET_INTERFACE interrupt: alt=%d intf=%d\n",
- dev->cur_alt, dev->cur_intf);
-
- /* programm the NE registers */
- for (i = 0; i < UDC_EP_NUM; i++) {
- ep = &dev->ep[i];
- if (ep->in) {
-
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num;
-
-
- /* OUT ep */
- } else {
- /* ep ix in UDC CSR register space */
- udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
- }
-
- /* UDC CSR reg */
- /* set ep values */
- tmp = readl(&dev->csr->ne[udc_csr_epix]);
- /* ep interface */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf,
- UDC_CSR_NE_INTF);
- /* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */
- /* ep alt */
- tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt,
- UDC_CSR_NE_ALT);
- /* write reg */
- writel(tmp, &dev->csr->ne[udc_csr_epix]);
-
- /* clear stall bits */
- ep->halted = 0;
- tmp = readl(&ep->regs->ctl);
- tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
- writel(tmp, &ep->regs->ctl);
- }
-
- /* call gadget zero with setup data received */
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
- spin_lock(&dev->lock);
-
- } /* USB reset */
- if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) {
- DBG(dev, "USB Reset interrupt\n");
- ret_val = IRQ_HANDLED;
-
- /* allow soft reset when suspend occurs */
- soft_reset_occured = 0;
-
- dev->waiting_zlp_ack_ep0in = 0;
- dev->set_cfg_not_acked = 0;
-
- /* mask not needed interrupts */
- udc_mask_unused_interrupts(dev);
-
- /* call gadget to resume and reset configs etc. */
- spin_unlock(&dev->lock);
- if (dev->sys_suspended && dev->driver->resume) {
- dev->driver->resume(&dev->gadget);
- dev->sys_suspended = 0;
- }
- usb_gadget_udc_reset(&dev->gadget, dev->driver);
- spin_lock(&dev->lock);
-
- /* disable ep0 to empty req queue */
- empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
- ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
-
- /* soft reset when rxfifo not empty */
- tmp = readl(&dev->regs->sts);
- if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
- && !soft_reset_after_usbreset_occured) {
- udc_soft_reset(dev);
- soft_reset_after_usbreset_occured++;
- }
-
- /*
- * DMA reset to kill potential old DMA hw hang,
- * POLL bit is already reset by ep_init() through
- * disconnect()
- */
- DBG(dev, "DMA machine reset\n");
- tmp = readl(&dev->regs->cfg);
- writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg);
- writel(tmp, &dev->regs->cfg);
-
- /* put into initial config */
- udc_basic_init(dev);
-
- /* enable device setup interrupts */
- udc_enable_dev_setup_interrupts(dev);
-
- /* enable suspend interrupt */
- tmp = readl(&dev->regs->irqmsk);
- tmp &= AMD_UNMASK_BIT(UDC_DEVINT_US);
- writel(tmp, &dev->regs->irqmsk);
-
- } /* USB suspend */
- if (dev_irq & AMD_BIT(UDC_DEVINT_US)) {
- DBG(dev, "USB Suspend interrupt\n");
- ret_val = IRQ_HANDLED;
- if (dev->driver->suspend) {
- spin_unlock(&dev->lock);
- dev->sys_suspended = 1;
- dev->driver->suspend(&dev->gadget);
- spin_lock(&dev->lock);
- }
- } /* new speed ? */
- if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) {
- DBG(dev, "ENUM interrupt\n");
- ret_val = IRQ_HANDLED;
- soft_reset_after_usbreset_occured = 0;
-
- /* disable ep0 to empty req queue */
- empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
- ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
-
- /* link up all endpoints */
- udc_setup_endpoints(dev);
- dev_info(&dev->pdev->dev, "Connect: %s\n",
- usb_speed_string(dev->gadget.speed));
-
- /* init ep 0 */
- activate_control_endpoints(dev);
-
- /* enable ep0 interrupts */
- udc_enable_ep0_interrupts(dev);
- }
- /* session valid change interrupt */
- if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) {
- DBG(dev, "USB SVC interrupt\n");
- ret_val = IRQ_HANDLED;
-
- /* check that session is not valid to detect disconnect */
- tmp = readl(&dev->regs->sts);
- if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) {
- /* disable suspend interrupt */
- tmp = readl(&dev->regs->irqmsk);
- tmp |= AMD_BIT(UDC_DEVINT_US);
- writel(tmp, &dev->regs->irqmsk);
- DBG(dev, "USB Disconnect (session valid low)\n");
- /* cleanup on disconnect */
- usb_disconnect(udc);
- }
-
- }
-
- return ret_val;
-}
-
-/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
-static irqreturn_t udc_irq(int irq, void *pdev)
-{
- struct udc *dev = pdev;
- u32 reg;
- u16 i;
- u32 ep_irq;
- irqreturn_t ret_val = IRQ_NONE;
-
- spin_lock(&dev->lock);
-
- /* check for ep irq */
- reg = readl(&dev->regs->ep_irqsts);
- if (reg) {
- if (reg & AMD_BIT(UDC_EPINT_OUT_EP0))
- ret_val |= udc_control_out_isr(dev);
- if (reg & AMD_BIT(UDC_EPINT_IN_EP0))
- ret_val |= udc_control_in_isr(dev);
-
- /*
- * data endpoint
- * iterate ep's
- */
- for (i = 1; i < UDC_EP_NUM; i++) {
- ep_irq = 1 << i;
- if (!(reg & ep_irq) || i == UDC_EPINT_OUT_EP0)
- continue;
-
- /* clear irq status */
- writel(ep_irq, &dev->regs->ep_irqsts);
-
- /* irq for out ep ? */
- if (i > UDC_EPIN_NUM)
- ret_val |= udc_data_out_isr(dev, i);
- else
- ret_val |= udc_data_in_isr(dev, i);
- }
-
- }
-
-
- /* check for dev irq */
- reg = readl(&dev->regs->irqsts);
- if (reg) {
- /* clear irq */
- writel(reg, &dev->regs->irqsts);
- ret_val |= udc_dev_isr(dev, reg);
- }
+ * The UDC is based on Synopsys designware core USB device controller IP.
+ * PCI device registration part of the driver is done here.
+ * Other tasks like Gagget/Endpoint ops are implemented in synopsys core
+ * driver file (snps_udc_core.c).
+ */
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
+#define UDC_DRIVER_VERSION_STRING "01.00.0206"
- spin_unlock(&dev->lock);
- return ret_val;
-}
+/* system */
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/prefetch.h>
+#include <linux/pci.h>
-/* Tears down device */
-static void gadget_release(struct device *pdev)
-{
- struct amd5536udc *dev = dev_get_drvdata(pdev);
- kfree(dev);
-}
+/* udc specific */
+#include "amd5536udc.h"
-/* Cleanup on device remove */
-static void udc_remove(struct udc *dev)
-{
- /* remove timer */
- stop_timer++;
- if (timer_pending(&udc_timer))
- wait_for_completion(&on_exit);
- if (udc_timer.data)
- del_timer_sync(&udc_timer);
- /* remove pollstall timer */
- stop_pollstall_timer++;
- if (timer_pending(&udc_pollstall_timer))
- wait_for_completion(&on_pollstall_exit);
- if (udc_pollstall_timer.data)
- del_timer_sync(&udc_pollstall_timer);
- udc = NULL;
-}
+/* pointer to device object */
+static struct udc *udc;
-/* free all the dma pools */
-static void free_dma_pools(struct udc *dev)
-{
- dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
- dev->ep[UDC_EP0OUT_IX].td_phys);
- dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
- dev->ep[UDC_EP0OUT_IX].td_stp_dma);
- dma_pool_destroy(dev->stp_requests);
- dma_pool_destroy(dev->data_requests);
-}
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = "amd5536udc";
/* Reset all pci context */
static void udc_pci_remove(struct pci_dev *pdev)
@@ -3146,137 +80,6 @@ static void udc_pci_remove(struct pci_dev *pdev)
udc_remove(dev);
}
-/* create dma pools on init */
-static int init_dma_pools(struct udc *dev)
-{
- struct udc_stp_dma *td_stp;
- struct udc_data_dma *td_data;
- int retval;
-
- /* consistent DMA mode setting ? */
- if (use_dma_ppb) {
- use_dma_bufferfill_mode = 0;
- } else {
- use_dma_ppb_du = 0;
- use_dma_bufferfill_mode = 1;
- }
-
- /* DMA setup */
- dev->data_requests = dma_pool_create("data_requests", NULL,
- sizeof(struct udc_data_dma), 0, 0);
- if (!dev->data_requests) {
- DBG(dev, "can't get request data pool\n");
- return -ENOMEM;
- }
-
- /* EP0 in dma regs = dev control regs */
- dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
-
- /* dma desc for setup data */
- dev->stp_requests = dma_pool_create("setup requests", NULL,
- sizeof(struct udc_stp_dma), 0, 0);
- if (!dev->stp_requests) {
- DBG(dev, "can't get stp request pool\n");
- retval = -ENOMEM;
- goto err_create_dma_pool;
- }
- /* setup */
- td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
- &dev->ep[UDC_EP0OUT_IX].td_stp_dma);
- if (!td_stp) {
- retval = -ENOMEM;
- goto err_alloc_dma;
- }
- dev->ep[UDC_EP0OUT_IX].td_stp = td_stp;
-
- /* data: 0 packets !? */
- td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
- &dev->ep[UDC_EP0OUT_IX].td_phys);
- if (!td_data) {
- retval = -ENOMEM;
- goto err_alloc_phys;
- }
- dev->ep[UDC_EP0OUT_IX].td = td_data;
- return 0;
-
-err_alloc_phys:
- dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
- dev->ep[UDC_EP0OUT_IX].td_stp_dma);
-err_alloc_dma:
- dma_pool_destroy(dev->stp_requests);
- dev->stp_requests = NULL;
-err_create_dma_pool:
- dma_pool_destroy(dev->data_requests);
- dev->data_requests = NULL;
- return retval;
-}
-
-/* general probe */
-static int udc_probe(struct udc *dev)
-{
- char tmp[128];
- u32 reg;
- int retval;
-
- /* mark timer as not initialized */
- udc_timer.data = 0;
- udc_pollstall_timer.data = 0;
-
- /* device struct setup */
- dev->gadget.ops = &udc_ops;
-
- dev_set_name(&dev->gadget.dev, "gadget");
- dev->gadget.name = name;
- dev->gadget.max_speed = USB_SPEED_HIGH;
-
- /* init registers, interrupts, ... */
- startup_registers(dev);
-
- dev_info(&dev->pdev->dev, "%s\n", mod_desc);
-
- snprintf(tmp, sizeof(tmp), "%d", dev->irq);
- dev_info(&dev->pdev->dev,
- "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
- tmp, dev->phys_addr, dev->chiprev,
- (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
- strcpy(tmp, UDC_DRIVER_VERSION_STRING);
- if (dev->chiprev == UDC_HSA0_REV) {
- dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
- retval = -ENODEV;
- goto finished;
- }
- dev_info(&dev->pdev->dev,
- "driver version: %s(for Geode5536 B1)\n", tmp);
- udc = dev;
-
- retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
- gadget_release);
- if (retval)
- goto finished;
-
- /* timer init */
- init_timer(&udc_timer);
- udc_timer.function = udc_timer_function;
- udc_timer.data = 1;
- /* timer pollstall init */
- init_timer(&udc_pollstall_timer);
- udc_pollstall_timer.function = udc_pollstall_timer_function;
- udc_pollstall_timer.data = 1;
-
- /* set SD */
- reg = readl(&dev->regs->ctl);
- reg |= AMD_BIT(UDC_DEVCTL_SD);
- writel(reg, &dev->regs->ctl);
-
- /* print dev register info */
- print_regs(dev);
-
- return 0;
-
-finished:
- return retval;
-}
-
/* Called by pci bus driver to init pci context */
static int udc_pci_probe(
struct pci_dev *pdev,
@@ -3405,7 +208,6 @@ static struct pci_driver udc_pci_driver = {
.probe = udc_pci_probe,
.remove = udc_pci_remove,
};
-
module_pci_driver(udc_pci_driver);
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index 4638d70..bd2a18e 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -13,6 +13,12 @@
#ifndef AMD5536UDC_H
#define AMD5536UDC_H
+/* debug control */
+/* #define UDC_VERBOSE */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
/* various constants */
#define UDC_RDE_TIMER_SECONDS 1
#define UDC_RDE_TIMER_DIV 10
@@ -567,6 +573,36 @@ union udc_setup_data {
struct usb_ctrlrequest request;
};
+/* Function declarations */
+int udc_enable_dev_setup_interrupts(struct udc *dev);
+int udc_mask_unused_interrupts(struct udc *dev);
+irqreturn_t udc_irq(int irq, void *pdev);
+void gadget_release(struct device *pdev);
+void udc_basic_init(struct udc *dev);
+void free_dma_pools(struct udc *dev);
+int init_dma_pools(struct udc *dev);
+void udc_remove(struct udc *dev);
+int udc_probe(struct udc *dev);
+
+/* DMA usage flag */
+static bool use_dma = 1;
+/* packet per buffer dma */
+static bool use_dma_ppb = 1;
+/* with per descr. update */
+static bool use_dma_ppb_du;
+/* full speed only mode */
+static bool use_fullspeed;
+
+/* module parameters */
+module_param(use_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma, "true for DMA");
+module_param(use_dma_ppb, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
+module_param(use_dma_ppb_du, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb_du,
+ "true for DMA in packet per buffer mode with descriptor update");
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/*
*---------------------------------------------------------------------------
* SET and GET bitfields in u32 values
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
new file mode 100644
index 0000000..69a94bc
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -0,0 +1,3216 @@
+/*
+ * snps_udc_core.c -- Synopsys UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller"
+#define UDC_DRIVER_VERSION_STRING "01.00.0206"
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/dmapool.h>
+#include <linux/moduleparam.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include "amd5536udc.h"
+
+static void udc_tasklet_disconnect(unsigned long);
+static void empty_req_queue(struct udc_ep *);
+static void udc_setup_endpoints(struct udc *dev);
+static void udc_soft_reset(struct udc *dev);
+static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
+static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
+
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = "amd5536udc";
+
+/* structure to hold endpoint function pointers */
+static const struct usb_ep_ops udc_ep_ops;
+
+/* received setup data */
+static union udc_setup_data setup_data;
+
+/* pointer to device object */
+static struct udc *udc;
+
+/* irq spin lock for soft reset */
+static DEFINE_SPINLOCK(udc_irq_spinlock);
+/* stall spin lock */
+static DEFINE_SPINLOCK(udc_stall_spinlock);
+
+/*
+* slave mode: pending bytes in rx fifo after nyet,
+* used if EPIN irq came but no req was available
+*/
+static unsigned int udc_rxfifo_pending;
+
+/* count soft resets after suspend to avoid loop */
+static int soft_reset_occured;
+static int soft_reset_after_usbreset_occured;
+
+/* timer */
+static struct timer_list udc_timer;
+static int stop_timer;
+
+/* set_rde -- Is used to control enabling of RX DMA. Problem is
+ * that UDC has only one bit (RDE) to enable/disable RX DMA for
+ * all OUT endpoints. So we have to handle race conditions like
+ * when OUT data reaches the fifo but no request was queued yet.
+ * This cannot be solved by letting the RX DMA disabled until a
+ * request gets queued because there may be other OUT packets
+ * in the FIFO (important for not blocking control traffic).
+ * The value of set_rde controls the correspondig timer.
+ *
+ * set_rde -1 == not used, means it is alloed to be set to 0 or 1
+ * set_rde 0 == do not touch RDE, do no start the RDE timer
+ * set_rde 1 == timer function will look whether FIFO has data
+ * set_rde 2 == set by timer function to enable RX DMA on next call
+ */
+static int set_rde = -1;
+
+static DECLARE_COMPLETION(on_exit);
+static struct timer_list udc_pollstall_timer;
+static int stop_pollstall_timer;
+static DECLARE_COMPLETION(on_pollstall_exit);
+
+/* tasklet for usb disconnect */
+static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
+ (unsigned long) &udc);
+
+
+/* endpoint names used for print */
+static const char ep0_string[] = "ep0in";
+static const struct {
+ const char *name;
+ const struct usb_ep_caps caps;
+} ep_info[] = {
+#define EP_INFO(_name, _caps) \
+ { \
+ .name = _name, \
+ .caps = _caps, \
+ }
+
+ EP_INFO(ep0_string,
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep1in-int",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep2in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep3in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep4in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep5in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep6in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep7in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep8in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep9in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep10in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep11in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep12in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep13in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep14in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep15in-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+ EP_INFO("ep0out",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep1out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep2out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep3out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep4out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep5out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep6out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep7out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep8out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep9out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep10out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep11out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep12out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep13out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep14out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+ EP_INFO("ep15out-bulk",
+ USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+
+#undef EP_INFO
+};
+
+/* buffer fill mode */
+static int use_dma_bufferfill_mode;
+/* tx buffer size for high speed */
+static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
+
+/*---------------------------------------------------------------------------*/
+/* Prints UDC device registers and endpoint irq registers */
+static void print_regs(struct udc *dev)
+{
+ DBG(dev, "------- Device registers -------\n");
+ DBG(dev, "dev config = %08x\n", readl(&dev->regs->cfg));
+ DBG(dev, "dev control = %08x\n", readl(&dev->regs->ctl));
+ DBG(dev, "dev status = %08x\n", readl(&dev->regs->sts));
+ DBG(dev, "\n");
+ DBG(dev, "dev int's = %08x\n", readl(&dev->regs->irqsts));
+ DBG(dev, "dev intmask = %08x\n", readl(&dev->regs->irqmsk));
+ DBG(dev, "\n");
+ DBG(dev, "dev ep int's = %08x\n", readl(&dev->regs->ep_irqsts));
+ DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk));
+ DBG(dev, "\n");
+ DBG(dev, "USE DMA = %d\n", use_dma);
+ if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
+ DBG(dev, "DMA mode = PPBNDU (packet per buffer "
+ "WITHOUT desc. update)\n");
+ dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
+ } else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
+ DBG(dev, "DMA mode = PPBDU (packet per buffer "
+ "WITH desc. update)\n");
+ dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
+ }
+ if (use_dma && use_dma_bufferfill_mode) {
+ DBG(dev, "DMA mode = BF (buffer fill mode)\n");
+ dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
+ }
+ if (!use_dma)
+ dev_info(&dev->pdev->dev, "FIFO mode\n");
+ DBG(dev, "-------------------------------------------------------\n");
+}
+
+/* Masks unused interrupts */
+int udc_mask_unused_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ /* mask all dev interrupts */
+ tmp = AMD_BIT(UDC_DEVINT_SVC) |
+ AMD_BIT(UDC_DEVINT_ENUM) |
+ AMD_BIT(UDC_DEVINT_US) |
+ AMD_BIT(UDC_DEVINT_UR) |
+ AMD_BIT(UDC_DEVINT_ES) |
+ AMD_BIT(UDC_DEVINT_SI) |
+ AMD_BIT(UDC_DEVINT_SOF)|
+ AMD_BIT(UDC_DEVINT_SC);
+ writel(tmp, &dev->regs->irqmsk);
+
+ /* mask all ep interrupts */
+ writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
+
+/* Enables endpoint 0 interrupts */
+static int udc_enable_ep0_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "udc_enable_ep0_interrupts()\n");
+
+ /* read irq mask */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ /* enable ep0 irq's */
+ tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)
+ & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);
+ writel(tmp, &dev->regs->ep_irqmsk);
+
+ return 0;
+}
+
+/* Enables device interrupts for SET_INTF and SET_CONFIG */
+int udc_enable_dev_setup_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "enable device interrupts for setup data\n");
+
+ /* read irq mask */
+ tmp = readl(&dev->regs->irqmsk);
+
+ /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
+ tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)
+ & AMD_UNMASK_BIT(UDC_DEVINT_SC)
+ & AMD_UNMASK_BIT(UDC_DEVINT_UR)
+ & AMD_UNMASK_BIT(UDC_DEVINT_SVC)
+ & AMD_UNMASK_BIT(UDC_DEVINT_ENUM);
+ writel(tmp, &dev->regs->irqmsk);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
+
+/* Calculates fifo start of endpoint based on preceding endpoints */
+static int udc_set_txfifo_addr(struct udc_ep *ep)
+{
+ struct udc *dev;
+ u32 tmp;
+ int i;
+
+ if (!ep || !(ep->in))
+ return -EINVAL;
+
+ dev = ep->dev;
+ ep->txfifo = dev->txfifo;
+
+ /* traverse ep's */
+ for (i = 0; i < ep->num; i++) {
+ if (dev->ep[i].regs) {
+ /* read fifo size */
+ tmp = readl(&dev->ep[i].regs->bufin_framenum);
+ tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
+ ep->txfifo += tmp;
+ }
+ }
+ return 0;
+}
+
+/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */
+static u32 cnak_pending;
+
+static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num)
+{
+ if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) {
+ DBG(ep->dev, "NAK could not be cleared for ep%d\n", num);
+ cnak_pending |= 1 << (num);
+ ep->naking = 1;
+ } else
+ cnak_pending = cnak_pending & (~(1 << (num)));
+}
+
+
+/* Enables endpoint, is called by gadget driver */
+static int
+udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
+{
+ struct udc_ep *ep;
+ struct udc *dev;
+ u32 tmp;
+ unsigned long iflags;
+ u8 udc_csr_epix;
+ unsigned maxpacket;
+
+ if (!usbep
+ || usbep->name == ep0_string
+ || !desc
+ || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ dev = ep->dev;
+
+ DBG(dev, "udc_ep_enable() ep %d\n", ep->num);
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ ep->ep.desc = desc;
+
+ ep->halted = 0;
+
+ /* set traffic type */
+ tmp = readl(&dev->ep[ep->num].regs->ctl);
+ tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
+ writel(tmp, &dev->ep[ep->num].regs->ctl);
+
+ /* set max packet size */
+ maxpacket = usb_endpoint_maxp(desc);
+ tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
+ tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
+ ep->ep.maxpacket = maxpacket;
+ writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);
+
+ /* IN ep */
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+ /* set buffer size (tx fifo entries) */
+ tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);
+ /* double buffering: fifo size = 2 x max packet size */
+ tmp = AMD_ADDBITS(
+ tmp,
+ maxpacket * UDC_EPIN_BUFF_SIZE_MULT
+ / UDC_DWORD_BYTES,
+ UDC_EPIN_BUFF_SIZE);
+ writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);
+
+ /* calc. tx fifo base addr */
+ udc_set_txfifo_addr(ep);
+
+ /* flush fifo */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_F);
+ writel(tmp, &ep->regs->ctl);
+
+ /* OUT ep */
+ } else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+
+ /* set max packet size UDC CSR */
+ tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+ tmp = AMD_ADDBITS(tmp, maxpacket,
+ UDC_CSR_NE_MAX_PKT);
+ writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+
+ if (use_dma && !ep->in) {
+ /* alloc and init BNA dummy request */
+ ep->bna_dummy_req = udc_alloc_bna_dummy(ep);
+ ep->bna_occurred = 0;
+ }
+
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_enabled = 1;
+ }
+
+ /* set ep values */
+ tmp = readl(&dev->csr->ne[udc_csr_epix]);
+ /* max packet */
+ tmp = AMD_ADDBITS(tmp, maxpacket, UDC_CSR_NE_MAX_PKT);
+ /* ep number */
+ tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
+ /* ep direction */
+ tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);
+ /* ep type */
+ tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
+ /* ep config */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
+ /* ep interface */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
+ /* ep alt */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
+ /* write reg */
+ writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* enable ep irq */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp &= AMD_UNMASK_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+
+ /*
+ * clear NAK by writing CNAK
+ * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written
+ */
+ if (!use_dma || ep->in) {
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+ tmp = desc->bEndpointAddress;
+ DBG(dev, "%s enabled\n", usbep->name);
+
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return 0;
+}
+
+/* Resets endpoint */
+static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
+{
+ u32 tmp;
+
+ VDBG(ep->dev, "ep-%d reset\n", ep->num);
+ ep->ep.desc = NULL;
+ ep->ep.ops = &udc_ep_ops;
+ INIT_LIST_HEAD(&ep->queue);
+
+ usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0);
+ /* set NAK */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_SNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 1;
+
+ /* disable interrupt */
+ tmp = readl(®s->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp, ®s->ep_irqmsk);
+
+ if (ep->in) {
+ /* unset P and IN bit of potential former DMA */
+ tmp = readl(&ep->regs->ctl);
+ tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);
+ writel(tmp, &ep->regs->ctl);
+
+ tmp = readl(&ep->regs->sts);
+ tmp |= AMD_BIT(UDC_EPSTS_IN);
+ writel(tmp, &ep->regs->sts);
+
+ /* flush the fifo */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_F);
+ writel(tmp, &ep->regs->ctl);
+
+ }
+ /* reset desc pointer */
+ writel(0, &ep->regs->desptr);
+}
+
+/* Disables endpoint, is called by gadget driver */
+static int udc_ep_disable(struct usb_ep *usbep)
+{
+ struct udc_ep *ep = NULL;
+ unsigned long iflags;
+
+ if (!usbep)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (usbep->name == ep0_string || !ep->ep.desc)
+ return -EINVAL;
+
+ DBG(ep->dev, "Disable ep-%d\n", ep->num);
+
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ udc_free_request(&ep->ep, &ep->bna_dummy_req->req);
+ empty_req_queue(ep);
+ ep_init(ep->dev->regs, ep);
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
+
+ return 0;
+}
+
+/* Allocates request packet, called by gadget driver */
+static struct usb_request *
+udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
+{
+ struct udc_request *req;
+ struct udc_data_dma *dma_desc;
+ struct udc_ep *ep;
+
+ if (!usbep)
+ return NULL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+
+ VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num);
+ req = kzalloc(sizeof(struct udc_request), gfp);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_DONT_USE;
+ INIT_LIST_HEAD(&req->queue);
+
+ if (ep->dma) {
+ /* ep0 in requests are allocated from data pool here */
+ dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
+ &req->td_phys);
+ if (!dma_desc) {
+ kfree(req);
+ return NULL;
+ }
+
+ VDBG(ep->dev, "udc_alloc_req: req = %p dma_desc = %p, "
+ "td_phys = %lx\n",
+ req, dma_desc,
+ (unsigned long)req->td_phys);
+ /* prevent from using desc. - set HOST BUSY */
+ dma_desc->status = AMD_ADDBITS(dma_desc->status,
+ UDC_DMA_STP_STS_BS_HOST_BUSY,
+ UDC_DMA_STP_STS_BS);
+ dma_desc->bufptr = cpu_to_le32(DMA_DONT_USE);
+ req->td_data = dma_desc;
+ req->td_data_last = NULL;
+ req->chain_len = 1;
+ }
+
+ return &req->req;
+}
+
+/* frees pci pool descriptors of a DMA chain */
+static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
+{
+ int ret_val = 0;
+ struct udc_data_dma *td;
+ struct udc_data_dma *td_last = NULL;
+ unsigned int i;
+
+ DBG(dev, "free chain req = %p\n", req);
+
+ /* do not free first desc., will be done by free for request */
+ td_last = req->td_data;
+ td = phys_to_virt(td_last->next);
+
+ for (i = 1; i < req->chain_len; i++) {
+ pci_pool_free(dev->data_requests, td,
+ (dma_addr_t)td_last->next);
+ td_last = td;
+ td = phys_to_virt(td_last->next);
+ }
+
+ return ret_val;
+}
+
+/* Frees request packet, called by gadget driver */
+static void
+udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+ struct udc_ep *ep;
+ struct udc_request *req;
+
+ if (!usbep || !usbreq)
+ return;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ req = container_of(usbreq, struct udc_request, req);
+ VDBG(ep->dev, "free_req req=%p\n", req);
+ BUG_ON(!list_empty(&req->queue));
+ if (req->td_data) {
+ VDBG(ep->dev, "req->td_data=%p\n", req->td_data);
+
+ /* free dma chain if created */
+ if (req->chain_len > 1)
+ udc_free_dma_chain(ep->dev, req);
+
+ pci_pool_free(ep->dev->data_requests, req->td_data,
+ req->td_phys);
+ }
+ kfree(req);
+}
+
+/* Init BNA dummy descriptor for HOST BUSY and pointing to itself */
+static void udc_init_bna_dummy(struct udc_request *req)
+{
+ if (req) {
+ /* set last bit */
+ req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+ /* set next pointer to itself */
+ req->td_data->next = req->td_phys;
+ /* set HOST BUSY */
+ req->td_data->status
+ = AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_DMA_DONE,
+ UDC_DMA_STP_STS_BS);
+#ifdef UDC_VERBOSE
+ pr_debug("bna desc = %p, sts = %08x\n",
+ req->td_data, req->td_data->status);
+#endif
+ }
+}
+
+/* Allocate BNA dummy descriptor */
+static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep)
+{
+ struct udc_request *req = NULL;
+ struct usb_request *_req = NULL;
+
+ /* alloc the dummy request */
+ _req = udc_alloc_request(&ep->ep, GFP_ATOMIC);
+ if (_req) {
+ req = container_of(_req, struct udc_request, req);
+ ep->bna_dummy_req = req;
+ udc_init_bna_dummy(req);
+ }
+ return req;
+}
+
+/* Write data to TX fifo for IN packets */
+static void
+udc_txfifo_write(struct udc_ep *ep, struct usb_request *req)
+{
+ u8 *req_buf;
+ u32 *buf;
+ int i, j;
+ unsigned bytes = 0;
+ unsigned remaining = 0;
+
+ if (!req || !ep)
+ return;
+
+ req_buf = req->buf + req->actual;
+ prefetch(req_buf);
+ remaining = req->length - req->actual;
+
+ buf = (u32 *) req_buf;
+
+ bytes = ep->ep.maxpacket;
+ if (bytes > remaining)
+ bytes = remaining;
+
+ /* dwords first */
+ for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
+ writel(*(buf + i), ep->txfifo);
+
+ /* remaining bytes must be written by byte access */
+ for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+ writeb((u8)(*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)),
+ ep->txfifo);
+ }
+
+ /* dummy write confirm */
+ writel(0, &ep->regs->confirm);
+}
+
+/* Read dwords from RX fifo for OUT transfers */
+static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords)
+{
+ int i;
+
+ VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords);
+
+ for (i = 0; i < dwords; i++)
+ *(buf + i) = readl(dev->rxfifo);
+ return 0;
+}
+
+/* Read bytes from RX fifo for OUT transfers */
+static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes)
+{
+ int i, j;
+ u32 tmp;
+
+ VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes);
+
+ /* dwords first */
+ for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
+ *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo);
+
+ /* remaining bytes must be read by byte access */
+ if (bytes % UDC_DWORD_BYTES) {
+ tmp = readl(dev->rxfifo);
+ for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+ *(buf + (i<<2) + j) = (u8)(tmp & UDC_BYTE_MASK);
+ tmp = tmp >> UDC_BITS_PER_BYTE;
+ }
+ }
+
+ return 0;
+}
+
+/* Read data from RX fifo for OUT transfers */
+static int
+udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req)
+{
+ u8 *buf;
+ unsigned buf_space;
+ unsigned bytes = 0;
+ unsigned finished = 0;
+
+ /* received number bytes */
+ bytes = readl(&ep->regs->sts);
+ bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE);
+
+ buf_space = req->req.length - req->req.actual;
+ buf = req->req.buf + req->req.actual;
+ if (bytes > buf_space) {
+ if ((buf_space % ep->ep.maxpacket) != 0) {
+ DBG(ep->dev,
+ "%s: rx %d bytes, rx-buf space = %d bytesn\n",
+ ep->ep.name, bytes, buf_space);
+ req->req.status = -EOVERFLOW;
+ }
+ bytes = buf_space;
+ }
+ req->req.actual += bytes;
+
+ /* last packet ? */
+ if (((bytes % ep->ep.maxpacket) != 0) || (!bytes)
+ || ((req->req.actual == req->req.length) && !req->req.zero))
+ finished = 1;
+
+ /* read rx fifo bytes */
+ VDBG(ep->dev, "ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes);
+ udc_rxfifo_read_bytes(ep->dev, buf, bytes);
+
+ return finished;
+}
+
+/* Creates or re-inits a DMA chain */
+static int udc_create_dma_chain(
+ struct udc_ep *ep,
+ struct udc_request *req,
+ unsigned long buf_len, gfp_t gfp_flags
+)
+{
+ unsigned long bytes = req->req.length;
+ unsigned int i;
+ dma_addr_t dma_addr;
+ struct udc_data_dma *td = NULL;
+ struct udc_data_dma *last = NULL;
+ unsigned long txbytes;
+ unsigned create_new_chain = 0;
+ unsigned len;
+
+ VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n",
+ bytes, buf_len);
+ dma_addr = DMA_DONT_USE;
+
+ /* unset L bit in first desc for OUT */
+ if (!ep->in)
+ req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
+
+ /* alloc only new desc's if not already available */
+ len = req->req.length / ep->ep.maxpacket;
+ if (req->req.length % ep->ep.maxpacket)
+ len++;
+
+ if (len > req->chain_len) {
+ /* shorter chain already allocated before */
+ if (req->chain_len > 1)
+ udc_free_dma_chain(ep->dev, req);
+ req->chain_len = len;
+ create_new_chain = 1;
+ }
+
+ td = req->td_data;
+ /* gen. required number of descriptors and buffers */
+ for (i = buf_len; i < bytes; i += buf_len) {
+ /* create or determine next desc. */
+ if (create_new_chain) {
+ td = pci_pool_alloc(ep->dev->data_requests,
+ gfp_flags, &dma_addr);
+ if (!td)
+ return -ENOMEM;
+
+ td->status = 0;
+ } else if (i == buf_len) {
+ /* first td */
+ td = (struct udc_data_dma *)phys_to_virt(
+ req->td_data->next);
+ td->status = 0;
+ } else {
+ td = (struct udc_data_dma *)phys_to_virt(last->next);
+ td->status = 0;
+ }
+
+ if (td)
+ td->bufptr = req->req.dma + i; /* assign buffer */
+ else
+ break;
+
+ /* short packet ? */
+ if ((bytes - i) >= buf_len) {
+ txbytes = buf_len;
+ } else {
+ /* short packet */
+ txbytes = bytes - i;
+ }
+
+ /* link td and assign tx bytes */
+ if (i == buf_len) {
+ if (create_new_chain)
+ req->td_data->next = dma_addr;
+ /*
+ * else
+ * req->td_data->next = virt_to_phys(td);
+ */
+ /* write tx bytes */
+ if (ep->in) {
+ /* first desc */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ ep->ep.maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+ /* second desc */
+ td->status = AMD_ADDBITS(td->status,
+ txbytes,
+ UDC_DMA_IN_STS_TXBYTES);
+ }
+ } else {
+ if (create_new_chain)
+ last->next = dma_addr;
+ /*
+ * else
+ * last->next = virt_to_phys(td);
+ */
+ if (ep->in) {
+ /* write tx bytes */
+ td->status = AMD_ADDBITS(td->status,
+ txbytes,
+ UDC_DMA_IN_STS_TXBYTES);
+ }
+ }
+ last = td;
+ }
+ /* set last bit */
+ if (td) {
+ td->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+ /* last desc. points to itself */
+ req->td_data_last = td;
+ }
+
+ return 0;
+}
+
+/* create/re-init a DMA descriptor or a DMA descriptor chain */
+static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp)
+{
+ int retval = 0;
+ u32 tmp;
+
+ VDBG(ep->dev, "prep_dma\n");
+ VDBG(ep->dev, "prep_dma ep%d req->td_data=%p\n",
+ ep->num, req->td_data);
+
+ /* set buffer pointer */
+ req->td_data->bufptr = req->req.dma;
+
+ /* set last bit */
+ req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+
+ /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */
+ if (use_dma_ppb) {
+
+ retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
+ if (retval != 0) {
+ if (retval == -ENOMEM)
+ DBG(ep->dev, "Out of DMA memory\n");
+ return retval;
+ }
+ if (ep->in) {
+ if (req->req.length == ep->ep.maxpacket) {
+ /* write tx bytes */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ ep->ep.maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+
+ }
+ }
+
+ }
+
+ if (ep->in) {
+ VDBG(ep->dev, "IN: use_dma_ppb=%d req->req.len=%d "
+ "maxpacket=%d ep%d\n",
+ use_dma_ppb, req->req.length,
+ ep->ep.maxpacket, ep->num);
+ /*
+ * if bytes < max packet then tx bytes must
+ * be written in packet per buffer mode
+ */
+ if (!use_dma_ppb || req->req.length < ep->ep.maxpacket
+ || ep->num == UDC_EP0OUT_IX
+ || ep->num == UDC_EP0IN_IX) {
+ /* write tx bytes */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ req->req.length,
+ UDC_DMA_IN_STS_TXBYTES);
+ /* reset frame num */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ 0,
+ UDC_DMA_IN_STS_FRAMENUM);
+ }
+ /* set HOST BUSY */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_BUSY,
+ UDC_DMA_STP_STS_BS);
+ } else {
+ VDBG(ep->dev, "OUT set host ready\n");
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_READY,
+ UDC_DMA_STP_STS_BS);
+
+
+ /* clear NAK by writing CNAK */
+ if (ep->naking) {
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+
+ }
+
+ return retval;
+}
+
+/* Completes request packet ... caller MUST hold lock */
+static void
+complete_req(struct udc_ep *ep, struct udc_request *req, int sts)
+__releases(ep->dev->lock)
+__acquires(ep->dev->lock)
+{
+ struct udc *dev;
+ unsigned halted;
+
+ VDBG(ep->dev, "complete_req(): ep%d\n", ep->num);
+
+ dev = ep->dev;
+ /* unmap DMA */
+ if (ep->dma)
+ usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
+
+ halted = ep->halted;
+ ep->halted = 1;
+
+ /* set new status if pending */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = sts;
+
+ /* remove from ep queue */
+ list_del_init(&req->queue);
+
+ VDBG(ep->dev, "req %p => complete %d bytes at %s with sts %d\n",
+ &req->req, req->req.length, ep->ep.name, sts);
+
+ spin_unlock(&dev->lock);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
+ spin_lock(&dev->lock);
+ ep->halted = halted;
+}
+
+/* Iterates to the end of a DMA chain and returns last descriptor */
+static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
+{
+ struct udc_data_dma *td;
+
+ td = req->td_data;
+ while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L)))
+ td = phys_to_virt(td->next);
+
+ return td;
+
+}
+
+/* Iterates to the end of a DMA chain and counts bytes received */
+static u32 udc_get_ppbdu_rxbytes(struct udc_request *req)
+{
+ struct udc_data_dma *td;
+ u32 count;
+
+ td = req->td_data;
+ /* received number bytes */
+ count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES);
+
+ while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) {
+ td = phys_to_virt(td->next);
+ /* received number bytes */
+ if (td) {
+ count += AMD_GETBITS(td->status,
+ UDC_DMA_OUT_STS_RXBYTES);
+ }
+ }
+
+ return count;
+
+}
+
+/* Enabling RX DMA */
+static void udc_set_rde(struct udc *dev)
+{
+ u32 tmp;
+
+ VDBG(dev, "udc_set_rde()\n");
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* set RDE */
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+ writel(tmp, &dev->regs->ctl);
+}
+
+/* Queues a request packet, called by gadget driver */
+static int
+udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
+{
+ int retval = 0;
+ u8 open_rxfifo = 0;
+ unsigned long iflags;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ struct udc *dev;
+ u32 tmp;
+
+ /* check the inputs */
+ req = container_of(usbreq, struct udc_request, req);
+
+ if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf
+ || !list_empty(&req->queue))
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+
+ VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in);
+ dev = ep->dev;
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* map dma (usually done before) */
+ if (ep->dma) {
+ VDBG(dev, "DMA map req %p\n", req);
+ retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in);
+ if (retval)
+ return retval;
+ }
+
+ VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n",
+ usbep->name, usbreq, usbreq->length,
+ req->td_data, usbreq->buf);
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ usbreq->actual = 0;
+ usbreq->status = -EINPROGRESS;
+ req->dma_done = 0;
+
+ /* on empty queue just do first transfer */
+ if (list_empty(&ep->queue)) {
+ /* zlp */
+ if (usbreq->length == 0) {
+ /* IN zlp's are handled by hardware */
+ complete_req(ep, req, 0);
+ VDBG(dev, "%s: zlp\n", ep->ep.name);
+ /*
+ * if set_config or set_intf is waiting for ack by zlp
+ * then set CSR_DONE
+ */
+ if (dev->set_cfg_not_acked) {
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
+ writel(tmp, &dev->regs->ctl);
+ dev->set_cfg_not_acked = 0;
+ }
+ /* setup command is ACK'ed now by zlp */
+ if (dev->waiting_zlp_ack_ep0in) {
+ /* clear NAK by writing CNAK in EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX],
+ UDC_EP0IN_IX);
+ dev->waiting_zlp_ack_ep0in = 0;
+ }
+ goto finished;
+ }
+ if (ep->dma) {
+ retval = prep_dma(ep, req, GFP_ATOMIC);
+ if (retval != 0)
+ goto finished;
+ /* write desc pointer to enable DMA */
+ if (ep->in) {
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(req->td_data->status,
+ UDC_DMA_IN_STS_BS_HOST_READY,
+ UDC_DMA_IN_STS_BS);
+ }
+
+ /* disabled rx dma while descriptor update */
+ if (!ep->in) {
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* clear RDE */
+ tmp = readl(&dev->regs->ctl);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+ writel(tmp, &dev->regs->ctl);
+ open_rxfifo = 1;
+
+ /*
+ * if BNA occurred then let BNA dummy desc.
+ * point to current desc.
+ */
+ if (ep->bna_occurred) {
+ VDBG(dev, "copy to BNA dummy desc.\n");
+ memcpy(ep->bna_dummy_req->td_data,
+ req->td_data,
+ sizeof(struct udc_data_dma));
+ }
+ }
+ /* write desc pointer */
+ writel(req->td_phys, &ep->regs->desptr);
+
+ /* clear NAK by writing CNAK */
+ if (ep->naking) {
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->naking = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+
+ if (ep->in) {
+ /* enable ep irq */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp &= AMD_UNMASK_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+ }
+ } else if (ep->in) {
+ /* enable ep irq */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp &= AMD_UNMASK_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+ }
+
+ } else if (ep->dma) {
+
+ /*
+ * prep_dma not used for OUT ep's, this is not possible
+ * for PPB modes, because of chain creation reasons
+ */
+ if (ep->in) {
+ retval = prep_dma(ep, req, GFP_ATOMIC);
+ if (retval != 0)
+ goto finished;
+ }
+ }
+ VDBG(dev, "list_add\n");
+ /* add request to ep queue */
+ if (req) {
+
+ list_add_tail(&req->queue, &ep->queue);
+
+ /* open rxfifo if out data queued */
+ if (open_rxfifo) {
+ /* enable DMA */
+ req->dma_going = 1;
+ udc_set_rde(dev);
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_queued = 1;
+ }
+ /* stop OUT naking */
+ if (!ep->in) {
+ if (!use_dma && udc_rxfifo_pending) {
+ DBG(dev, "udc_queue(): pending bytes in "
+ "rxfifo after nyet\n");
+ /*
+ * read pending bytes afer nyet:
+ * referring to isr
+ */
+ if (udc_rxfifo_read(ep, req)) {
+ /* finish */
+ complete_req(ep, req, 0);
+ }
+ udc_rxfifo_pending = 0;
+
+ }
+ }
+ }
+
+finished:
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return retval;
+}
+
+/* Empty request queue of an endpoint; caller holds spinlock */
+static void empty_req_queue(struct udc_ep *ep)
+{
+ struct udc_request *req;
+
+ ep->halted = 1;
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct udc_request,
+ queue);
+ complete_req(ep, req, -ESHUTDOWN);
+ }
+}
+
+/* Dequeues a request packet, called by gadget driver */
+static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned halted;
+ unsigned long iflags;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0
+ && ep->num != UDC_EP0OUT_IX)))
+ return -EINVAL;
+
+ req = container_of(usbreq, struct udc_request, req);
+
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ halted = ep->halted;
+ ep->halted = 1;
+ /* request in processing or next one */
+ if (ep->queue.next == &req->queue) {
+ if (ep->dma && req->dma_going) {
+ if (ep->in)
+ ep->cancel_transfer = 1;
+ else {
+ u32 tmp;
+ u32 dma_sts;
+ /* stop potential receive DMA */
+ tmp = readl(&udc->regs->ctl);
+ writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_RDE),
+ &udc->regs->ctl);
+ /*
+ * Cancel transfer later in ISR
+ * if descriptor was touched.
+ */
+ dma_sts = AMD_GETBITS(req->td_data->status,
+ UDC_DMA_OUT_STS_BS);
+ if (dma_sts != UDC_DMA_OUT_STS_BS_HOST_READY)
+ ep->cancel_transfer = 1;
+ else {
+ udc_init_bna_dummy(ep->req);
+ writel(ep->bna_dummy_req->td_phys,
+ &ep->regs->desptr);
+ }
+ writel(tmp, &udc->regs->ctl);
+ }
+ }
+ }
+ complete_req(ep, req, -ECONNRESET);
+ ep->halted = halted;
+
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
+ return 0;
+}
+
+/* Halt or clear halt of endpoint */
+static int
+udc_set_halt(struct usb_ep *usbep, int halt)
+{
+ struct udc_ep *ep;
+ u32 tmp;
+ unsigned long iflags;
+ int retval = 0;
+
+ if (!usbep)
+ return -EINVAL;
+
+ pr_debug("set_halt %s: halt=%d\n", usbep->name, halt);
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
+ /* halt or clear halt */
+ if (halt) {
+ if (ep->num == 0)
+ ep->dev->stall_ep0in = 1;
+ else {
+ /*
+ * set STALL
+ * rxfifo empty not taken into acount
+ */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ ep->halted = 1;
+
+ /* setup poll timer */
+ if (!timer_pending(&udc_pollstall_timer)) {
+ udc_pollstall_timer.expires = jiffies +
+ HZ * UDC_POLLSTALL_TIMER_USECONDS
+ / (1000 * 1000);
+ if (!stop_pollstall_timer) {
+ DBG(ep->dev, "start polltimer\n");
+ add_timer(&udc_pollstall_timer);
+ }
+ }
+ }
+ } else {
+ /* ep is halted by set_halt() before */
+ if (ep->halted) {
+ tmp = readl(&ep->regs->ctl);
+ /* clear stall bit */
+ tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+ /* clear NAK by writing CNAK */
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->halted = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+ }
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+ return retval;
+}
+
+/* gadget interface */
+static const struct usb_ep_ops udc_ep_ops = {
+ .enable = udc_ep_enable,
+ .disable = udc_ep_disable,
+
+ .alloc_request = udc_alloc_request,
+ .free_request = udc_free_request,
+
+ .queue = udc_queue,
+ .dequeue = udc_dequeue,
+
+ .set_halt = udc_set_halt,
+ /* fifo ops not implemented */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Get frame counter (not implemented) */
+static int udc_get_frame(struct usb_gadget *gadget)
+{
+ return -EOPNOTSUPP;
+}
+
+/* Initiates a remote wakeup */
+static int udc_remote_wakeup(struct udc *dev)
+{
+ unsigned long flags;
+ u32 tmp;
+
+ DBG(dev, "UDC initiates remote wakeup\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_RES);
+ writel(tmp, &dev->regs->ctl);
+ tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES);
+ writel(tmp, &dev->regs->ctl);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+
+/* Remote wakeup gadget interface */
+static int udc_wakeup(struct usb_gadget *gadget)
+{
+ struct udc *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct udc, gadget);
+ udc_remote_wakeup(dev);
+
+ return 0;
+}
+
+static int amd5536_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int amd5536_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops udc_ops = {
+ .wakeup = udc_wakeup,
+ .get_frame = udc_get_frame,
+ .udc_start = amd5536_udc_start,
+ .udc_stop = amd5536_udc_stop,
+};
+
+/* Setups endpoint parameters, adds endpoints to linked list */
+static void make_ep_lists(struct udc *dev)
+{
+ /* make gadget ep lists */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+
+ /* fifo config */
+ dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE;
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE;
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf;
+ dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE;
+}
+
+/* Inits UDC context */
+void udc_basic_init(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "udc_basic_init()\n");
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* stop poll stall timer */
+ if (timer_pending(&udc_pollstall_timer))
+ mod_timer(&udc_pollstall_timer, jiffies - 1);
+ /* disable DMA */
+ tmp = readl(&dev->regs->ctl);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE);
+ writel(tmp, &dev->regs->ctl);
+
+ /* enable dynamic CSR programming */
+ tmp = readl(&dev->regs->cfg);
+ tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG);
+ /* set self powered */
+ tmp |= AMD_BIT(UDC_DEVCFG_SP);
+ /* set remote wakeupable */
+ tmp |= AMD_BIT(UDC_DEVCFG_RWKP);
+ writel(tmp, &dev->regs->cfg);
+
+ make_ep_lists(dev);
+
+ dev->data_ep_enabled = 0;
+ dev->data_ep_queued = 0;
+}
+EXPORT_SYMBOL_GPL(udc_basic_init);
+
+/* init registers at driver load time */
+static int startup_registers(struct udc *dev)
+{
+ u32 tmp;
+
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* put into initial config */
+ udc_basic_init(dev);
+ /* link up all endpoints */
+ udc_setup_endpoints(dev);
+
+ /* program speed */
+ tmp = readl(&dev->regs->cfg);
+ if (use_fullspeed)
+ tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+ else
+ tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
+ writel(tmp, &dev->regs->cfg);
+
+ return 0;
+}
+
+/* Sets initial endpoint parameters */
+static void udc_setup_endpoints(struct udc *dev)
+{
+ struct udc_ep *ep;
+ u32 tmp;
+ u32 reg;
+
+ DBG(dev, "udc_setup_endpoints()\n");
+
+ /* read enum speed */
+ tmp = readl(&dev->regs->sts);
+ tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
+ if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
+ dev->gadget.speed = USB_SPEED_HIGH;
+ else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
+ dev->gadget.speed = USB_SPEED_FULL;
+
+ /* set basic ep parameters */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+ ep = &dev->ep[tmp];
+ ep->dev = dev;
+ ep->ep.name = ep_info[tmp].name;
+ ep->ep.caps = ep_info[tmp].caps;
+ ep->num = tmp;
+ /* txfifo size is calculated at enable time */
+ ep->txfifo = dev->txfifo;
+
+ /* fifo size */
+ if (tmp < UDC_EPIN_NUM) {
+ ep->fifo_depth = UDC_TXFIFO_SIZE;
+ ep->in = 1;
+ } else {
+ ep->fifo_depth = UDC_RXFIFO_SIZE;
+ ep->in = 0;
+
+ }
+ ep->regs = &dev->ep_regs[tmp];
+ /*
+ * ep will be reset only if ep was not enabled before to avoid
+ * disabling ep interrupts when ENUM interrupt occurs but ep is
+ * not enabled by gadget driver
+ */
+ if (!ep->ep.desc)
+ ep_init(dev->regs, ep);
+
+ if (use_dma) {
+ /*
+ * ep->dma is not really used, just to indicate that
+ * DMA is active: remove this
+ * dma regs = dev control regs
+ */
+ ep->dma = &dev->regs->ctl;
+
+ /* nak OUT endpoints until enable - not for ep0 */
+ if (tmp != UDC_EP0IN_IX && tmp != UDC_EP0OUT_IX
+ && tmp > UDC_EPIN_NUM) {
+ /* set NAK */
+ reg = readl(&dev->ep[tmp].regs->ctl);
+ reg |= AMD_BIT(UDC_EPCTL_SNAK);
+ writel(reg, &dev->ep[tmp].regs->ctl);
+ dev->ep[tmp].naking = 1;
+
+ }
+ }
+ }
+ /* EP0 max packet */
+ if (dev->gadget.speed == USB_SPEED_FULL) {
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+ UDC_FS_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+ UDC_FS_EP0OUT_MAX_PKT_SIZE);
+ } else if (dev->gadget.speed == USB_SPEED_HIGH) {
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+ UDC_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+ UDC_EP0OUT_MAX_PKT_SIZE);
+ }
+
+ /*
+ * with suspend bug workaround, ep0 params for gadget driver
+ * are set at gadget driver bind() call
+ */
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ dev->ep[UDC_EP0IN_IX].halted = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* init cfg/alt/int */
+ dev->cur_config = 0;
+ dev->cur_intf = 0;
+ dev->cur_alt = 0;
+}
+
+/* Bringup after Connect event, initial bringup to be ready for ep0 events */
+static void usb_connect(struct udc *dev)
+{
+
+ dev_info(&dev->pdev->dev, "USB Connect\n");
+
+ dev->connected = 1;
+
+ /* put into initial config */
+ udc_basic_init(dev);
+
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+}
+
+/*
+ * Calls gadget with disconnect event and resets the UDC and makes
+ * initial bringup to be ready for ep0 events
+ */
+static void usb_disconnect(struct udc *dev)
+{
+
+ dev_info(&dev->pdev->dev, "USB Disconnect\n");
+
+ dev->connected = 0;
+
+ /* mask interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* REVISIT there doesn't seem to be a point to having this
+ * talk to a tasklet ... do it directly, we already hold
+ * the spinlock needed to process the disconnect.
+ */
+
+ tasklet_schedule(&disconnect_tasklet);
+}
+
+/* Tasklet for disconnect to be outside of interrupt context */
+static void udc_tasklet_disconnect(unsigned long par)
+{
+ struct udc *dev = (struct udc *)(*((struct udc **) par));
+ u32 tmp;
+
+ DBG(dev, "Tasklet disconnect\n");
+ spin_lock_irq(&dev->lock);
+
+ if (dev->driver) {
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+
+ /* empty queues */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&dev->ep[tmp]);
+
+ }
+
+ /* disable ep0 */
+ ep_init(dev->regs,
+ &dev->ep[UDC_EP0IN_IX]);
+
+
+ if (!soft_reset_occured) {
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+ soft_reset_occured++;
+ }
+
+ /* re-enable dev interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+ /* back to full speed ? */
+ if (use_fullspeed) {
+ tmp = readl(&dev->regs->cfg);
+ tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+ writel(tmp, &dev->regs->cfg);
+ }
+
+ spin_unlock_irq(&dev->lock);
+}
+
+/* Reset the UDC core */
+static void udc_soft_reset(struct udc *dev)
+{
+ unsigned long flags;
+
+ DBG(dev, "Soft reset\n");
+ /*
+ * reset possible waiting interrupts, because int.
+ * status is lost after soft reset,
+ * ep int. status reset
+ */
+ writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
+ /* device int. status reset */
+ writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
+
+ spin_lock_irqsave(&udc_irq_spinlock, flags);
+ writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+ readl(&dev->regs->cfg);
+ spin_unlock_irqrestore(&udc_irq_spinlock, flags);
+
+}
+
+/* RDE timer callback to set RDE bit */
+static void udc_timer_function(unsigned long v)
+{
+ u32 tmp;
+
+ spin_lock_irq(&udc_irq_spinlock);
+
+ if (set_rde > 0) {
+ /*
+ * open the fifo if fifo was filled on last timer call
+ * conditionally
+ */
+ if (set_rde > 1) {
+ /* set RDE to receive setup data */
+ tmp = readl(&udc->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+ writel(tmp, &udc->regs->ctl);
+ set_rde = -1;
+ } else if (readl(&udc->regs->sts)
+ & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
+ /*
+ * if fifo empty setup polling, do not just
+ * open the fifo
+ */
+ udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ } else {
+ /*
+ * fifo contains data now, setup timer for opening
+ * the fifo when timer expires to be able to receive
+ * setup packets, when data packets gets queued by
+ * gadget layer then timer will forced to expire with
+ * set_rde=0 (RDE is set in udc_queue())
+ */
+ set_rde++;
+ /* debug: lhadmot_timer_start = 221070 */
+ udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+
+ } else
+ set_rde = -1; /* RDE was set by udc_queue() */
+ spin_unlock_irq(&udc_irq_spinlock);
+ if (stop_timer)
+ complete(&on_exit);
+
+}
+
+/* Handle halt state, used in stall poll timer */
+static void udc_handle_halt_state(struct udc_ep *ep)
+{
+ u32 tmp;
+ /* set stall as long not halted */
+ if (ep->halted == 1) {
+ tmp = readl(&ep->regs->ctl);
+ /* STALL cleared ? */
+ if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
+ /*
+ * FIXME: MSC spec requires that stall remains
+ * even on receivng of CLEAR_FEATURE HALT. So
+ * we would set STALL again here to be compliant.
+ * But with current mass storage drivers this does
+ * not work (would produce endless host retries).
+ * So we clear halt on CLEAR_FEATURE.
+ *
+ DBG(ep->dev, "ep %d: set STALL again\n", ep->num);
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);*/
+
+ /* clear NAK by writing CNAK */
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &ep->regs->ctl);
+ ep->halted = 0;
+ UDC_QUEUE_CNAK(ep, ep->num);
+ }
+ }
+}
+
+/* Stall timer callback to poll S bit and set it again after */
+static void udc_pollstall_timer_function(unsigned long v)
+{
+ struct udc_ep *ep;
+ int halted = 0;
+
+ spin_lock_irq(&udc_stall_spinlock);
+ /*
+ * only one IN and OUT endpoints are handled
+ * IN poll stall
+ */
+ ep = &udc->ep[UDC_EPIN_IX];
+ udc_handle_halt_state(ep);
+ if (ep->halted)
+ halted = 1;
+ /* OUT poll stall */
+ ep = &udc->ep[UDC_EPOUT_IX];
+ udc_handle_halt_state(ep);
+ if (ep->halted)
+ halted = 1;
+
+ /* setup timer again when still halted */
+ if (!stop_pollstall_timer && halted) {
+ udc_pollstall_timer.expires = jiffies +
+ HZ * UDC_POLLSTALL_TIMER_USECONDS
+ / (1000 * 1000);
+ add_timer(&udc_pollstall_timer);
+ }
+ spin_unlock_irq(&udc_stall_spinlock);
+
+ if (stop_pollstall_timer)
+ complete(&on_pollstall_exit);
+}
+
+/* Inits endpoint 0 so that SETUP packets are processed */
+static void activate_control_endpoints(struct udc *dev)
+{
+ u32 tmp;
+
+ DBG(dev, "activate_control_endpoints\n");
+
+ /* flush fifo */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_F);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+ /* set ep0 directions */
+ dev->ep[UDC_EP0IN_IX].in = 1;
+ dev->ep[UDC_EP0OUT_IX].in = 0;
+
+ /* set buffer size (tx fifo entries) of EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE,
+ UDC_EPIN_BUFF_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE,
+ UDC_EPIN_BUFF_SIZE);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+
+ /* set max packet size of EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+
+ /* set max packet size of EP0_OUT */
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+
+ /* set max packet size of EP0 in UDC CSR */
+ tmp = readl(&dev->csr->ne[0]);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+ UDC_CSR_NE_MAX_PKT);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+ UDC_CSR_NE_MAX_PKT);
+ writel(tmp, &dev->csr->ne[0]);
+
+ if (use_dma) {
+ dev->ep[UDC_EP0OUT_IX].td->status |=
+ AMD_BIT(UDC_DMA_OUT_STS_L);
+ /* write dma desc address */
+ writel(dev->ep[UDC_EP0OUT_IX].td_stp_dma,
+ &dev->ep[UDC_EP0OUT_IX].regs->subptr);
+ writel(dev->ep[UDC_EP0OUT_IX].td_phys,
+ &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+ /* stop RDE timer */
+ if (timer_pending(&udc_timer)) {
+ set_rde = 0;
+ mod_timer(&udc_timer, jiffies - 1);
+ }
+ /* stop pollstall timer */
+ if (timer_pending(&udc_pollstall_timer))
+ mod_timer(&udc_pollstall_timer, jiffies - 1);
+ /* enable DMA */
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_MODE)
+ | AMD_BIT(UDC_DEVCTL_RDE)
+ | AMD_BIT(UDC_DEVCTL_TDE);
+ if (use_dma_bufferfill_mode)
+ tmp |= AMD_BIT(UDC_DEVCTL_BF);
+ else if (use_dma_ppb_du)
+ tmp |= AMD_BIT(UDC_DEVCTL_DU);
+ writel(tmp, &dev->regs->ctl);
+ }
+
+ /* clear NAK by writing CNAK for EP0IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+
+ /* clear NAK by writing CNAK for EP0OUT */
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ dev->ep[UDC_EP0OUT_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+}
+
+/* Make endpoint 0 ready for control traffic */
+static int setup_ep0(struct udc *dev)
+{
+ activate_control_endpoints(dev);
+ /* enable ep0 interrupts */
+ udc_enable_ep0_interrupts(dev);
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+
+ return 0;
+}
+
+/* Called by gadget driver to register itself */
+static int amd5536_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct udc *dev = to_amd5536_udc(g);
+ u32 tmp;
+
+ driver->driver.bus = NULL;
+ dev->driver = driver;
+
+ /* Some gadget drivers use both ep0 directions.
+ * NOTE: to gadget driver, ep0 is just one endpoint...
+ */
+ dev->ep[UDC_EP0OUT_IX].ep.driver_data =
+ dev->ep[UDC_EP0IN_IX].ep.driver_data;
+
+ /* get ready for ep0 traffic */
+ setup_ep0(dev);
+
+ /* clear SD */
+ tmp = readl(&dev->regs->ctl);
+ tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD);
+ writel(tmp, &dev->regs->ctl);
+
+ usb_connect(dev);
+
+ return 0;
+}
+
+/* shutdown requests and disconnect from gadget */
+static void
+shutdown(struct udc *dev, struct usb_gadget_driver *driver)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+ int tmp;
+
+ /* empty queues and init hardware */
+ udc_basic_init(dev);
+
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&dev->ep[tmp]);
+
+ udc_setup_endpoints(dev);
+}
+
+/* Called by gadget driver to unregister itself */
+static int amd5536_udc_stop(struct usb_gadget *g)
+{
+ struct udc *dev = to_amd5536_udc(g);
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ udc_mask_unused_interrupts(dev);
+ shutdown(dev, NULL);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ dev->driver = NULL;
+
+ /* set SD */
+ tmp = readl(&dev->regs->ctl);
+ tmp |= AMD_BIT(UDC_DEVCTL_SD);
+ writel(tmp, &dev->regs->ctl);
+
+ return 0;
+}
+
+/* Clear pending NAK bits */
+static void udc_process_cnak_queue(struct udc *dev)
+{
+ u32 tmp;
+ u32 reg;
+
+ /* check epin's */
+ DBG(dev, "CNAK pending queue processing\n");
+ for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) {
+ if (cnak_pending & (1 << tmp)) {
+ DBG(dev, "CNAK pending for ep%d\n", tmp);
+ /* clear NAK by writing CNAK */
+ reg = readl(&dev->ep[tmp].regs->ctl);
+ reg |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(reg, &dev->ep[tmp].regs->ctl);
+ dev->ep[tmp].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num);
+ }
+ }
+ /* ... and ep0out */
+ if (cnak_pending & (1 << UDC_EP0OUT_IX)) {
+ DBG(dev, "CNAK pending for ep%d\n", UDC_EP0OUT_IX);
+ /* clear NAK by writing CNAK */
+ reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ reg |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ dev->ep[UDC_EP0OUT_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX],
+ dev->ep[UDC_EP0OUT_IX].num);
+ }
+}
+
+/* Enabling RX DMA after setup packet */
+static void udc_ep0_set_rde(struct udc *dev)
+{
+ if (use_dma) {
+ /*
+ * only enable RXDMA when no data endpoint enabled
+ * or data is queued
+ */
+ if (!dev->data_ep_enabled || dev->data_ep_queued) {
+ udc_set_rde(dev);
+ } else {
+ /*
+ * setup timer for enabling RDE (to not enable
+ * RXFIFO DMA for data endpoints to early)
+ */
+ if (set_rde != 0 && !timer_pending(&udc_timer)) {
+ udc_timer.expires =
+ jiffies + HZ/UDC_RDE_TIMER_DIV;
+ set_rde = 1;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+ }
+ }
+}
+
+
+/* Interrupt handler for data OUT traffic */
+static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned int count;
+ struct udc_data_dma *td = NULL;
+ unsigned dma_done;
+
+ VDBG(dev, "ep%d irq\n", ep_ix);
+ ep = &dev->ep[ep_ix];
+
+ tmp = readl(&ep->regs->sts);
+ if (use_dma) {
+ /* BNA event ? */
+ if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+ DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n",
+ ep->num, readl(&ep->regs->desptr));
+ /* clear BNA */
+ writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts);
+ if (!ep->cancel_transfer)
+ ep->bna_occurred = 1;
+ else
+ ep->cancel_transfer = 0;
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+ }
+ /* HE event ? */
+ if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
+ dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
+
+ /* clear HE */
+ writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+
+ if (!list_empty(&ep->queue)) {
+
+ /* next request */
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ } else {
+ req = NULL;
+ udc_rxfifo_pending = 1;
+ }
+ VDBG(dev, "req = %p\n", req);
+ /* fifo mode */
+ if (!use_dma) {
+
+ /* read fifo */
+ if (req && udc_rxfifo_read(ep, req)) {
+ ret_val = IRQ_HANDLED;
+
+ /* finish */
+ complete_req(ep, req, 0);
+ /* next request */
+ if (!list_empty(&ep->queue) && !ep->halted) {
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ } else
+ req = NULL;
+ }
+
+ /* DMA */
+ } else if (!ep->cancel_transfer && req) {
+ ret_val = IRQ_HANDLED;
+
+ /* check for DMA done */
+ if (!use_dma_ppb) {
+ dma_done = AMD_GETBITS(req->td_data->status,
+ UDC_DMA_OUT_STS_BS);
+ /* packet per buffer mode - rx bytes */
+ } else {
+ /*
+ * if BNA occurred then recover desc. from
+ * BNA dummy desc.
+ */
+ if (ep->bna_occurred) {
+ VDBG(dev, "Recover desc. from BNA dummy\n");
+ memcpy(req->td_data, ep->bna_dummy_req->td_data,
+ sizeof(struct udc_data_dma));
+ ep->bna_occurred = 0;
+ udc_init_bna_dummy(ep->req);
+ }
+ td = udc_get_last_dma_desc(req);
+ dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS);
+ }
+ if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
+ /* buffer fill mode - rx bytes */
+ if (!use_dma_ppb) {
+ /* received number bytes */
+ count = AMD_GETBITS(req->td_data->status,
+ UDC_DMA_OUT_STS_RXBYTES);
+ VDBG(dev, "rx bytes=%u\n", count);
+ /* packet per buffer mode - rx bytes */
+ } else {
+ VDBG(dev, "req->td_data=%p\n", req->td_data);
+ VDBG(dev, "last desc = %p\n", td);
+ /* received number bytes */
+ if (use_dma_ppb_du) {
+ /* every desc. counts bytes */
+ count = udc_get_ppbdu_rxbytes(req);
+ } else {
+ /* last desc. counts bytes */
+ count = AMD_GETBITS(td->status,
+ UDC_DMA_OUT_STS_RXBYTES);
+ if (!count && req->req.length
+ == UDC_DMA_MAXPACKET) {
+ /*
+ * on 64k packets the RXBYTES
+ * field is zero
+ */
+ count = UDC_DMA_MAXPACKET;
+ }
+ }
+ VDBG(dev, "last desc rx bytes=%u\n", count);
+ }
+
+ tmp = req->req.length - req->req.actual;
+ if (count > tmp) {
+ if ((tmp % ep->ep.maxpacket) != 0) {
+ DBG(dev, "%s: rx %db, space=%db\n",
+ ep->ep.name, count, tmp);
+ req->req.status = -EOVERFLOW;
+ }
+ count = tmp;
+ }
+ req->req.actual += count;
+ req->dma_going = 0;
+ /* complete request */
+ complete_req(ep, req, 0);
+
+ /* next request */
+ if (!list_empty(&ep->queue) && !ep->halted) {
+ req = list_entry(ep->queue.next,
+ struct udc_request,
+ queue);
+ /*
+ * DMA may be already started by udc_queue()
+ * called by gadget drivers completion
+ * routine. This happens when queue
+ * holds one request only.
+ */
+ if (req->dma_going == 0) {
+ /* next dma */
+ if (prep_dma(ep, req, GFP_ATOMIC) != 0)
+ goto finished;
+ /* write desc pointer */
+ writel(req->td_phys,
+ &ep->regs->desptr);
+ req->dma_going = 1;
+ /* enable DMA */
+ udc_set_rde(dev);
+ }
+ } else {
+ /*
+ * implant BNA dummy descriptor to allow
+ * RXFIFO opening by RDE
+ */
+ if (ep->bna_dummy_req) {
+ /* write desc pointer */
+ writel(ep->bna_dummy_req->td_phys,
+ &ep->regs->desptr);
+ ep->bna_occurred = 0;
+ }
+
+ /*
+ * schedule timer for setting RDE if queue
+ * remains empty to allow ep0 packets pass
+ * through
+ */
+ if (set_rde != 0
+ && !timer_pending(&udc_timer)) {
+ udc_timer.expires =
+ jiffies
+ + HZ*UDC_RDE_TIMER_SECONDS;
+ set_rde = 1;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_queued = 0;
+ }
+
+ } else {
+ /*
+ * RX DMA must be reenabled for each desc in PPBDU mode
+ * and must be enabled for PPBNDU mode in case of BNA
+ */
+ udc_set_rde(dev);
+ }
+
+ } else if (ep->cancel_transfer) {
+ ret_val = IRQ_HANDLED;
+ ep->cancel_transfer = 0;
+ }
+
+ /* check pending CNAKS */
+ if (cnak_pending) {
+ /* CNAk processing when rxfifo empty only */
+ if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+ udc_process_cnak_queue(dev);
+ }
+
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR, &ep->regs->sts);
+finished:
+ return ret_val;
+}
+
+/* Interrupt handler for data IN traffic */
+static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ u32 epsts;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ struct udc_data_dma *td;
+ unsigned len;
+
+ ep = &dev->ep[ep_ix];
+
+ epsts = readl(&ep->regs->sts);
+ if (use_dma) {
+ /* BNA ? */
+ if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
+ dev_err(&dev->pdev->dev,
+ "BNA ep%din occurred - DESPTR = %08lx\n",
+ ep->num,
+ (unsigned long) readl(&ep->regs->desptr));
+
+ /* clear BNA */
+ writel(epsts, &ep->regs->sts);
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+ }
+ /* HE event ? */
+ if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
+ dev_err(&dev->pdev->dev,
+ "HE ep%dn occurred - DESPTR = %08lx\n",
+ ep->num, (unsigned long) readl(&ep->regs->desptr));
+
+ /* clear HE */
+ writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+
+ /* DMA completion */
+ if (epsts & AMD_BIT(UDC_EPSTS_TDC)) {
+ VDBG(dev, "TDC set- completion\n");
+ ret_val = IRQ_HANDLED;
+ if (!ep->cancel_transfer && !list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ /*
+ * length bytes transferred
+ * check dma done of last desc. in PPBDU mode
+ */
+ if (use_dma_ppb_du) {
+ td = udc_get_last_dma_desc(req);
+ if (td)
+ req->req.actual = req->req.length;
+ } else {
+ /* assume all bytes transferred */
+ req->req.actual = req->req.length;
+ }
+
+ if (req->req.actual == req->req.length) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ req->dma_going = 0;
+ /* further request available ? */
+ if (list_empty(&ep->queue)) {
+ /* disable interrupt */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
+ }
+ }
+ }
+ ep->cancel_transfer = 0;
+
+ }
+ /*
+ * status reg has IN bit set and TDC not set (if TDC was handled,
+ * IN must not be handled (UDC defect) ?
+ */
+ if ((epsts & AMD_BIT(UDC_EPSTS_IN))
+ && !(epsts & AMD_BIT(UDC_EPSTS_TDC))) {
+ ret_val = IRQ_HANDLED;
+ if (!list_empty(&ep->queue)) {
+ /* next request */
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+ /* FIFO mode */
+ if (!use_dma) {
+ /* write fifo */
+ udc_txfifo_write(ep, &req->req);
+ len = req->req.length - req->req.actual;
+ if (len > ep->ep.maxpacket)
+ len = ep->ep.maxpacket;
+ req->req.actual += len;
+ if (req->req.actual == req->req.length
+ || (len != ep->ep.maxpacket)) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ }
+ /* DMA */
+ } else if (req && !req->dma_going) {
+ VDBG(dev, "IN DMA : req=%p req->td_data=%p\n",
+ req, req->td_data);
+ if (req->td_data) {
+
+ req->dma_going = 1;
+
+ /*
+ * unset L bit of first desc.
+ * for chain
+ */
+ if (use_dma_ppb && req->req.length >
+ ep->ep.maxpacket) {
+ req->td_data->status &=
+ AMD_CLEAR_BIT(
+ UDC_DMA_IN_STS_L);
+ }
+
+ /* write desc pointer */
+ writel(req->td_phys, &ep->regs->desptr);
+
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(
+ req->td_data->status,
+ UDC_DMA_IN_STS_BS_HOST_READY,
+ UDC_DMA_IN_STS_BS);
+
+ /* set poll demand bit */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_P);
+ writel(tmp, &ep->regs->ctl);
+ }
+ }
+
+ } else if (!use_dma && ep->in) {
+ /* disable interrupt */
+ tmp = readl(
+ &dev->regs->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp,
+ &dev->regs->ep_irqmsk);
+ }
+ }
+ /* clear status bits */
+ writel(epsts, &ep->regs->sts);
+
+finished:
+ return ret_val;
+
+}
+
+/* Interrupt handler for Control OUT traffic */
+static irqreturn_t udc_control_out_isr(struct udc *dev)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ int setup_supported;
+ u32 count;
+ int set = 0;
+ struct udc_ep *ep;
+ struct udc_ep *ep_tmp;
+
+ ep = &dev->ep[UDC_EP0OUT_IX];
+
+ /* clear irq */
+ writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts);
+
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+ /* check BNA and clear if set */
+ if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+ VDBG(dev, "ep0: BNA set\n");
+ writel(AMD_BIT(UDC_EPSTS_BNA),
+ &dev->ep[UDC_EP0OUT_IX].regs->sts);
+ ep->bna_occurred = 1;
+ ret_val = IRQ_HANDLED;
+ goto finished;
+ }
+
+ /* type of data: SETUP or DATA 0 bytes */
+ tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT);
+ VDBG(dev, "data_typ = %x\n", tmp);
+
+ /* setup data */
+ if (tmp == UDC_EPSTS_OUT_SETUP) {
+ ret_val = IRQ_HANDLED;
+
+ ep->dev->stall_ep0in = 0;
+ dev->waiting_zlp_ack_ep0in = 0;
+
+ /* set NAK for EP0_IN */
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_SNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 1;
+ /* get setup data */
+ if (use_dma) {
+
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR,
+ &dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+ setup_data.data[0] =
+ dev->ep[UDC_EP0OUT_IX].td_stp->data12;
+ setup_data.data[1] =
+ dev->ep[UDC_EP0OUT_IX].td_stp->data34;
+ /* set HOST READY */
+ dev->ep[UDC_EP0OUT_IX].td_stp->status =
+ UDC_DMA_STP_STS_BS_HOST_READY;
+ } else {
+ /* read fifo */
+ udc_rxfifo_read_dwords(dev, setup_data.data, 2);
+ }
+
+ /* determine direction of control data */
+ if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) {
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ /* enable RDE */
+ udc_ep0_set_rde(dev);
+ set = 0;
+ } else {
+ dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep;
+ /*
+ * implant BNA dummy descriptor to allow RXFIFO opening
+ * by RDE
+ */
+ if (ep->bna_dummy_req) {
+ /* write desc pointer */
+ writel(ep->bna_dummy_req->td_phys,
+ &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+ ep->bna_occurred = 0;
+ }
+
+ set = 1;
+ dev->ep[UDC_EP0OUT_IX].naking = 1;
+ /*
+ * setup timer for enabling RDE (to not enable
+ * RXFIFO DMA for data to early)
+ */
+ set_rde = 1;
+ if (!timer_pending(&udc_timer)) {
+ udc_timer.expires = jiffies +
+ HZ/UDC_RDE_TIMER_DIV;
+ if (!stop_timer)
+ add_timer(&udc_timer);
+ }
+ }
+
+ /*
+ * mass storage reset must be processed here because
+ * next packet may be a CLEAR_FEATURE HALT which would not
+ * clear the stall bit when no STALL handshake was received
+ * before (autostall can cause this)
+ */
+ if (setup_data.data[0] == UDC_MSCRES_DWORD0
+ && setup_data.data[1] == UDC_MSCRES_DWORD1) {
+ DBG(dev, "MSC Reset\n");
+ /*
+ * clear stall bits
+ * only one IN and OUT endpoints are handled
+ */
+ ep_tmp = &udc->ep[UDC_EPIN_IX];
+ udc_set_halt(&ep_tmp->ep, 0);
+ ep_tmp = &udc->ep[UDC_EPOUT_IX];
+ udc_set_halt(&ep_tmp->ep, 0);
+ }
+
+ /* call gadget with setup data received */
+ spin_unlock(&dev->lock);
+ setup_supported = dev->driver->setup(&dev->gadget,
+ &setup_data.request);
+ spin_lock(&dev->lock);
+
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ /* ep0 in returns data (not zlp) on IN phase */
+ if (setup_supported >= 0 && setup_supported <
+ UDC_EP0IN_MAXPACKET) {
+ /* clear NAK by writing CNAK in EP0_IN */
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ dev->ep[UDC_EP0IN_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+
+ /* if unsupported request then stall */
+ } else if (setup_supported < 0) {
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+ } else
+ dev->waiting_zlp_ack_ep0in = 1;
+
+
+ /* clear NAK by writing CNAK in EP0_OUT */
+ if (!set) {
+ tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+ writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+ dev->ep[UDC_EP0OUT_IX].naking = 0;
+ UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+ }
+
+ if (!use_dma) {
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR,
+ &dev->ep[UDC_EP0OUT_IX].regs->sts);
+ }
+
+ /* data packet 0 bytes */
+ } else if (tmp == UDC_EPSTS_OUT_DATA) {
+ /* clear OUT bits in ep status */
+ writel(UDC_EPSTS_OUT_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+ /* get setup data: only 0 packet */
+ if (use_dma) {
+ /* no req if 0 packet, just reactivate */
+ if (list_empty(&dev->ep[UDC_EP0OUT_IX].queue)) {
+ VDBG(dev, "ZLP\n");
+
+ /* set HOST READY */
+ dev->ep[UDC_EP0OUT_IX].td->status =
+ AMD_ADDBITS(
+ dev->ep[UDC_EP0OUT_IX].td->status,
+ UDC_DMA_OUT_STS_BS_HOST_READY,
+ UDC_DMA_OUT_STS_BS);
+ /* enable RDE */
+ udc_ep0_set_rde(dev);
+ ret_val = IRQ_HANDLED;
+
+ } else {
+ /* control write */
+ ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
+ /* re-program desc. pointer for possible ZLPs */
+ writel(dev->ep[UDC_EP0OUT_IX].td_phys,
+ &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+ /* enable RDE */
+ udc_ep0_set_rde(dev);
+ }
+ } else {
+
+ /* received number bytes */
+ count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+ count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE);
+ /* out data for fifo mode not working */
+ count = 0;
+
+ /* 0 packet or real data ? */
+ if (count != 0) {
+ ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
+ } else {
+ /* dummy read confirm */
+ readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm);
+ ret_val = IRQ_HANDLED;
+ }
+ }
+ }
+
+ /* check pending CNAKS */
+ if (cnak_pending) {
+ /* CNAk processing when rxfifo empty only */
+ if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+ udc_process_cnak_queue(dev);
+ }
+
+finished:
+ return ret_val;
+}
+
+/* Interrupt handler for Control IN traffic */
+static irqreturn_t udc_control_in_isr(struct udc *dev)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned len;
+
+ ep = &dev->ep[UDC_EP0IN_IX];
+
+ /* clear irq */
+ writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts);
+
+ tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
+ /* DMA completion */
+ if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
+ VDBG(dev, "isr: TDC clear\n");
+ ret_val = IRQ_HANDLED;
+
+ /* clear TDC bit */
+ writel(AMD_BIT(UDC_EPSTS_TDC),
+ &dev->ep[UDC_EP0IN_IX].regs->sts);
+
+ /* status reg has IN bit set ? */
+ } else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
+ ret_val = IRQ_HANDLED;
+
+ if (ep->dma) {
+ /* clear IN bit */
+ writel(AMD_BIT(UDC_EPSTS_IN),
+ &dev->ep[UDC_EP0IN_IX].regs->sts);
+ }
+ if (dev->stall_ep0in) {
+ DBG(dev, "stall ep0in\n");
+ /* halt ep0in */
+ tmp = readl(&ep->regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ } else {
+ if (!list_empty(&ep->queue)) {
+ /* next request */
+ req = list_entry(ep->queue.next,
+ struct udc_request, queue);
+
+ if (ep->dma) {
+ /* write desc pointer */
+ writel(req->td_phys, &ep->regs->desptr);
+ /* set HOST READY */
+ req->td_data->status =
+ AMD_ADDBITS(
+ req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_READY,
+ UDC_DMA_STP_STS_BS);
+
+ /* set poll demand bit */
+ tmp =
+ readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+ tmp |= AMD_BIT(UDC_EPCTL_P);
+ writel(tmp,
+ &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+ /* all bytes will be transferred */
+ req->req.actual = req->req.length;
+
+ /* complete req */
+ complete_req(ep, req, 0);
+
+ } else {
+ /* write fifo */
+ udc_txfifo_write(ep, &req->req);
+
+ /* lengh bytes transferred */
+ len = req->req.length - req->req.actual;
+ if (len > ep->ep.maxpacket)
+ len = ep->ep.maxpacket;
+
+ req->req.actual += len;
+ if (req->req.actual == req->req.length
+ || (len != ep->ep.maxpacket)) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ }
+ }
+
+ }
+ }
+ ep->halted = 0;
+ dev->stall_ep0in = 0;
+ if (!ep->dma) {
+ /* clear IN bit */
+ writel(AMD_BIT(UDC_EPSTS_IN),
+ &dev->ep[UDC_EP0IN_IX].regs->sts);
+ }
+ }
+
+ return ret_val;
+}
+
+
+/* Interrupt handler for global device events */
+static irqreturn_t udc_dev_isr(struct udc *dev, u32 dev_irq)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+ irqreturn_t ret_val = IRQ_NONE;
+ u32 tmp;
+ u32 cfg;
+ struct udc_ep *ep;
+ u16 i;
+ u8 udc_csr_epix;
+
+ /* SET_CONFIG irq ? */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) {
+ ret_val = IRQ_HANDLED;
+
+ /* read config value */
+ tmp = readl(&dev->regs->sts);
+ cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG);
+ DBG(dev, "SET_CONFIG interrupt: config=%d\n", cfg);
+ dev->cur_config = cfg;
+ dev->set_cfg_not_acked = 1;
+
+ /* make usb request for gadget driver */
+ memset(&setup_data, 0 , sizeof(union udc_setup_data));
+ setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
+ setup_data.request.wValue = cpu_to_le16(dev->cur_config);
+
+ /* programm the NE registers */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+
+ /* OUT ep */
+ } else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+ }
+
+ tmp = readl(&dev->csr->ne[udc_csr_epix]);
+ /* ep cfg */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_config,
+ UDC_CSR_NE_CFG);
+ /* write reg */
+ writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* clear stall bits */
+ ep->halted = 0;
+ tmp = readl(&ep->regs->ctl);
+ tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ }
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+ spin_lock(&dev->lock);
+
+ } /* SET_INTERFACE ? */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) {
+ ret_val = IRQ_HANDLED;
+
+ dev->set_cfg_not_acked = 1;
+ /* read interface and alt setting values */
+ tmp = readl(&dev->regs->sts);
+ dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT);
+ dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF);
+
+ /* make usb request for gadget driver */
+ memset(&setup_data, 0 , sizeof(union udc_setup_data));
+ setup_data.request.bRequest = USB_REQ_SET_INTERFACE;
+ setup_data.request.bRequestType = USB_RECIP_INTERFACE;
+ setup_data.request.wValue = cpu_to_le16(dev->cur_alt);
+ setup_data.request.wIndex = cpu_to_le16(dev->cur_intf);
+
+ DBG(dev, "SET_INTERFACE interrupt: alt=%d intf=%d\n",
+ dev->cur_alt, dev->cur_intf);
+
+ /* programm the NE registers */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+
+ /* OUT ep */
+ } else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+ }
+
+ /* UDC CSR reg */
+ /* set ep values */
+ tmp = readl(&dev->csr->ne[udc_csr_epix]);
+ /* ep interface */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf,
+ UDC_CSR_NE_INTF);
+ /* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */
+ /* ep alt */
+ tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt,
+ UDC_CSR_NE_ALT);
+ /* write reg */
+ writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* clear stall bits */
+ ep->halted = 0;
+ tmp = readl(&ep->regs->ctl);
+ tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+ writel(tmp, &ep->regs->ctl);
+ }
+
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+ spin_lock(&dev->lock);
+
+ } /* USB reset */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) {
+ DBG(dev, "USB Reset interrupt\n");
+ ret_val = IRQ_HANDLED;
+
+ /* allow soft reset when suspend occurs */
+ soft_reset_occured = 0;
+
+ dev->waiting_zlp_ack_ep0in = 0;
+ dev->set_cfg_not_acked = 0;
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* call gadget to resume and reset configs etc. */
+ spin_unlock(&dev->lock);
+ if (dev->sys_suspended && dev->driver->resume) {
+ dev->driver->resume(&dev->gadget);
+ dev->sys_suspended = 0;
+ }
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
+ spin_lock(&dev->lock);
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+ /* soft reset when rxfifo not empty */
+ tmp = readl(&dev->regs->sts);
+ if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+ && !soft_reset_after_usbreset_occured) {
+ udc_soft_reset(dev);
+ soft_reset_after_usbreset_occured++;
+ }
+
+ /*
+ * DMA reset to kill potential old DMA hw hang,
+ * POLL bit is already reset by ep_init() through
+ * disconnect()
+ */
+ DBG(dev, "DMA machine reset\n");
+ tmp = readl(&dev->regs->cfg);
+ writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg);
+ writel(tmp, &dev->regs->cfg);
+
+ /* put into initial config */
+ udc_basic_init(dev);
+
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+
+ /* enable suspend interrupt */
+ tmp = readl(&dev->regs->irqmsk);
+ tmp &= AMD_UNMASK_BIT(UDC_DEVINT_US);
+ writel(tmp, &dev->regs->irqmsk);
+
+ } /* USB suspend */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_US)) {
+ DBG(dev, "USB Suspend interrupt\n");
+ ret_val = IRQ_HANDLED;
+ if (dev->driver->suspend) {
+ spin_unlock(&dev->lock);
+ dev->sys_suspended = 1;
+ dev->driver->suspend(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+ } /* new speed ? */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) {
+ DBG(dev, "ENUM interrupt\n");
+ ret_val = IRQ_HANDLED;
+ soft_reset_after_usbreset_occured = 0;
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+ /* link up all endpoints */
+ udc_setup_endpoints(dev);
+ dev_info(&dev->pdev->dev, "Connect: %s\n",
+ usb_speed_string(dev->gadget.speed));
+
+ /* init ep 0 */
+ activate_control_endpoints(dev);
+
+ /* enable ep0 interrupts */
+ udc_enable_ep0_interrupts(dev);
+ }
+ /* session valid change interrupt */
+ if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) {
+ DBG(dev, "USB SVC interrupt\n");
+ ret_val = IRQ_HANDLED;
+
+ /* check that session is not valid to detect disconnect */
+ tmp = readl(&dev->regs->sts);
+ if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) {
+ /* disable suspend interrupt */
+ tmp = readl(&dev->regs->irqmsk);
+ tmp |= AMD_BIT(UDC_DEVINT_US);
+ writel(tmp, &dev->regs->irqmsk);
+ DBG(dev, "USB Disconnect (session valid low)\n");
+ /* cleanup on disconnect */
+ usb_disconnect(udc);
+ }
+
+ }
+
+ return ret_val;
+}
+
+/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
+irqreturn_t udc_irq(int irq, void *pdev)
+{
+ struct udc *dev = pdev;
+ u32 reg;
+ u16 i;
+ u32 ep_irq;
+ irqreturn_t ret_val = IRQ_NONE;
+
+ spin_lock(&dev->lock);
+
+ /* check for ep irq */
+ reg = readl(&dev->regs->ep_irqsts);
+ if (reg) {
+ if (reg & AMD_BIT(UDC_EPINT_OUT_EP0))
+ ret_val |= udc_control_out_isr(dev);
+ if (reg & AMD_BIT(UDC_EPINT_IN_EP0))
+ ret_val |= udc_control_in_isr(dev);
+
+ /*
+ * data endpoint
+ * iterate ep's
+ */
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ ep_irq = 1 << i;
+ if (!(reg & ep_irq) || i == UDC_EPINT_OUT_EP0)
+ continue;
+
+ /* clear irq status */
+ writel(ep_irq, &dev->regs->ep_irqsts);
+
+ /* irq for out ep ? */
+ if (i > UDC_EPIN_NUM)
+ ret_val |= udc_data_out_isr(dev, i);
+ else
+ ret_val |= udc_data_in_isr(dev, i);
+ }
+
+ }
+
+
+ /* check for dev irq */
+ reg = readl(&dev->regs->irqsts);
+ if (reg) {
+ /* clear irq */
+ writel(reg, &dev->regs->irqsts);
+ ret_val |= udc_dev_isr(dev, reg);
+ }
+
+
+ spin_unlock(&dev->lock);
+ return ret_val;
+}
+EXPORT_SYMBOL_GPL(udc_irq);
+
+/* Tears down device */
+void gadget_release(struct device *pdev)
+{
+ struct amd5536udc *dev = dev_get_drvdata(pdev);
+ kfree(dev);
+}
+EXPORT_SYMBOL_GPL(gadget_release);
+
+/* Cleanup on device remove */
+void udc_remove(struct udc *dev)
+{
+ /* remove timer */
+ stop_timer++;
+ if (timer_pending(&udc_timer))
+ wait_for_completion(&on_exit);
+ if (udc_timer.data)
+ del_timer_sync(&udc_timer);
+ /* remove pollstall timer */
+ stop_pollstall_timer++;
+ if (timer_pending(&udc_pollstall_timer))
+ wait_for_completion(&on_pollstall_exit);
+ if (udc_pollstall_timer.data)
+ del_timer_sync(&udc_pollstall_timer);
+ udc = NULL;
+}
+EXPORT_SYMBOL_GPL(udc_remove);
+
+/* free all the dma pools */
+void free_dma_pools(struct udc *dev)
+{
+ dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
+ dev->ep[UDC_EP0OUT_IX].td_phys);
+ dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
+ dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+ dma_pool_destroy(dev->stp_requests);
+ dma_pool_destroy(dev->data_requests);
+}
+EXPORT_SYMBOL_GPL(free_dma_pools);
+
+/* create dma pools on init */
+int init_dma_pools(struct udc *dev)
+{
+ struct udc_stp_dma *td_stp;
+ struct udc_data_dma *td_data;
+ int retval;
+
+ /* consistent DMA mode setting ? */
+ if (use_dma_ppb) {
+ use_dma_bufferfill_mode = 0;
+ } else {
+ use_dma_ppb_du = 0;
+ use_dma_bufferfill_mode = 1;
+ }
+
+ /* DMA setup */
+ dev->data_requests = dma_pool_create("data_requests", NULL,
+ sizeof(struct udc_data_dma), 0, 0);
+ if (!dev->data_requests) {
+ DBG(dev, "can't get request data pool\n");
+ return -ENOMEM;
+ }
+
+ /* EP0 in dma regs = dev control regs */
+ dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
+
+ /* dma desc for setup data */
+ dev->stp_requests = dma_pool_create("setup requests", NULL,
+ sizeof(struct udc_stp_dma), 0, 0);
+ if (!dev->stp_requests) {
+ DBG(dev, "can't get stp request pool\n");
+ retval = -ENOMEM;
+ goto err_create_dma_pool;
+ }
+ /* setup */
+ td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+ &dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+ if (!td_stp) {
+ retval = -ENOMEM;
+ goto err_alloc_dma;
+ }
+ dev->ep[UDC_EP0OUT_IX].td_stp = td_stp;
+
+ /* data: 0 packets !? */
+ td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+ &dev->ep[UDC_EP0OUT_IX].td_phys);
+ if (!td_data) {
+ retval = -ENOMEM;
+ goto err_alloc_phys;
+ }
+ dev->ep[UDC_EP0OUT_IX].td = td_data;
+ return 0;
+
+err_alloc_phys:
+ dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
+ dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+err_alloc_dma:
+ dma_pool_destroy(dev->stp_requests);
+ dev->stp_requests = NULL;
+err_create_dma_pool:
+ dma_pool_destroy(dev->data_requests);
+ dev->data_requests = NULL;
+ return retval;
+}
+EXPORT_SYMBOL_GPL(init_dma_pools);
+
+/* general probe */
+int udc_probe(struct udc *dev)
+{
+ char tmp[128];
+ u32 reg;
+ int retval;
+
+ /* mark timer as not initialized */
+ udc_timer.data = 0;
+ udc_pollstall_timer.data = 0;
+
+ /* device struct setup */
+ dev->gadget.ops = &udc_ops;
+
+ dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.name = name;
+ dev->gadget.max_speed = USB_SPEED_HIGH;
+
+ /* init registers, interrupts, ... */
+ startup_registers(dev);
+
+ dev_info(&dev->pdev->dev, "%s\n", mod_desc);
+
+ snprintf(tmp, sizeof(tmp), "%d", dev->irq);
+ dev_info(&dev->pdev->dev,
+ "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
+ tmp, dev->phys_addr, dev->chiprev,
+ (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
+ strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+ if (dev->chiprev == UDC_HSA0_REV) {
+ dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
+ retval = -ENODEV;
+ goto finished;
+ }
+ dev_info(&dev->pdev->dev,
+ "driver version: %s(for Geode5536 B1)\n", tmp);
+ udc = dev;
+
+ retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
+ gadget_release);
+ if (retval)
+ goto finished;
+
+ /* timer init */
+ init_timer(&udc_timer);
+ udc_timer.function = udc_timer_function;
+ udc_timer.data = 1;
+ /* timer pollstall init */
+ init_timer(&udc_pollstall_timer);
+ udc_pollstall_timer.function = udc_pollstall_timer_function;
+ udc_pollstall_timer.data = 1;
+
+ /* set SD */
+ reg = readl(&dev->regs->ctl);
+ reg |= AMD_BIT(UDC_DEVCTL_SD);
+ writel(reg, &dev->regs->ctl);
+
+ /* print dev register info */
+ print_regs(dev);
+
+ return 0;
+
+finished:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(udc_probe);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Thomas Dahlmann");
+MODULE_LICENSE("GPL");
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC v2 2/5] UDC: make debug prints compatible with both pci and platform devices
From: Raviteja Garimella @ 2017-01-17 8:05 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1484640308-25976-1-git-send-email-raviteja.garimella@broadcom.com>
This patch adds a struct device member to UDC data structure and
makes changes to the arguments of dev_err and dev_dbg calls so that
the debug prints work for both pci and platform devices.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/usb/gadget/udc/amd5536udc.c | 1 +
drivers/usb/gadget/udc/amd5536udc.h | 4 +++-
drivers/usb/gadget/udc/snps_udc_core.c | 28 ++++++++++++++--------------
3 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index c0acb80..3e66b08 100644
--- a/drivers/usb/gadget/udc/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
@@ -166,6 +166,7 @@ static int udc_pci_probe(
dev->phys_addr = resource;
dev->irq = pdev->irq;
dev->pdev = pdev;
+ dev->dev = &pdev->dev;
/* general probing */
if (udc_probe(dev)) {
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index bd2a18e..c252457 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -563,6 +563,8 @@ struct udc {
u16 cur_config;
u16 cur_intf;
u16 cur_alt;
+
+ struct device *dev;
};
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
@@ -639,7 +641,7 @@ MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/* debug macros ------------------------------------------------------------*/
-#define DBG(udc , args...) dev_dbg(&(udc)->pdev->dev, args)
+#define DBG(udc , args...) dev_dbg(udc->dev, args)
#ifdef UDC_VERBOSE
#define VDBG DBG
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 69a94bc..b7a6218 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -202,18 +202,18 @@ static void print_regs(struct udc *dev)
if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
DBG(dev, "DMA mode = PPBNDU (packet per buffer "
"WITHOUT desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
+ dev_info(dev->dev, "DMA mode (%s)\n", "PPBNDU");
} else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
DBG(dev, "DMA mode = PPBDU (packet per buffer "
"WITH desc. update)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
+ dev_info(dev->dev, "DMA mode (%s)\n", "PPBDU");
}
if (use_dma && use_dma_bufferfill_mode) {
DBG(dev, "DMA mode = BF (buffer fill mode)\n");
- dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
+ dev_info(dev->dev, "DMA mode (%s)\n", "BF");
}
if (!use_dma)
- dev_info(&dev->pdev->dev, "FIFO mode\n");
+ dev_info(dev->dev, "FIFO mode\n");
DBG(dev, "-------------------------------------------------------\n");
}
@@ -1621,7 +1621,7 @@ static void udc_setup_endpoints(struct udc *dev)
static void usb_connect(struct udc *dev)
{
- dev_info(&dev->pdev->dev, "USB Connect\n");
+ dev_info(dev->dev, "USB Connect\n");
dev->connected = 1;
@@ -1639,7 +1639,7 @@ static void usb_connect(struct udc *dev)
static void usb_disconnect(struct udc *dev)
{
- dev_info(&dev->pdev->dev, "USB Disconnect\n");
+ dev_info(dev->dev, "USB Disconnect\n");
dev->connected = 0;
@@ -2103,7 +2103,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
}
/* HE event ? */
if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
+ dev_err(dev->dev, "HE ep%dout occurred\n", ep->num);
/* clear HE */
writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
@@ -2302,7 +2302,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
if (use_dma) {
/* BNA ? */
if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
- dev_err(&dev->pdev->dev,
+ dev_err(dev->dev,
"BNA ep%din occurred - DESPTR = %08lx\n",
ep->num,
(unsigned long) readl(&ep->regs->desptr));
@@ -2315,7 +2315,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
}
/* HE event ? */
if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
- dev_err(&dev->pdev->dev,
+ dev_err(dev->dev,
"HE ep%dn occurred - DESPTR = %08lx\n",
ep->num, (unsigned long) readl(&ep->regs->desptr));
@@ -2953,7 +2953,7 @@ __acquires(dev->lock)
/* link up all endpoints */
udc_setup_endpoints(dev);
- dev_info(&dev->pdev->dev, "Connect: %s\n",
+ dev_info(dev->dev, "Connect: %s\n",
usb_speed_string(dev->gadget.speed));
/* init ep 0 */
@@ -3165,20 +3165,20 @@ int udc_probe(struct udc *dev)
/* init registers, interrupts, ... */
startup_registers(dev);
- dev_info(&dev->pdev->dev, "%s\n", mod_desc);
+ dev_info(dev->dev, "%s\n", mod_desc);
snprintf(tmp, sizeof(tmp), "%d", dev->irq);
- dev_info(&dev->pdev->dev,
+ dev_info(dev->dev,
"irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
tmp, dev->phys_addr, dev->chiprev,
(dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
strcpy(tmp, UDC_DRIVER_VERSION_STRING);
if (dev->chiprev == UDC_HSA0_REV) {
- dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
+ dev_err(dev->dev, "chip revision is A0; too old\n");
retval = -ENODEV;
goto finished;
}
- dev_info(&dev->pdev->dev,
+ dev_info(dev->dev,
"driver version: %s(for Geode5536 B1)\n", tmp);
udc = dev;
--
2.1.0
^ permalink raw reply related
* [RFC v2 3/5] UDC: Provide correct arguments for 'dma_pool_create'
From: Raviteja Garimella @ 2017-01-17 8:05 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree, linux-kernel, bcm-kernel-feedback-list, linux-usb
In-Reply-To: <1484640308-25976-1-git-send-email-raviteja.garimella@broadcom.com>
Change the argument from NULL to a struct device for the
dma_pool_create call during dma init.
Signed-off-by: Raviteja Garimella <raviteja.garimella@broadcom.com>
---
drivers/usb/gadget/udc/snps_udc_core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index b7a6218..556fa0e 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3094,7 +3094,7 @@ int init_dma_pools(struct udc *dev)
}
/* DMA setup */
- dev->data_requests = dma_pool_create("data_requests", NULL,
+ dev->data_requests = dma_pool_create("data_requests", dev->dev,
sizeof(struct udc_data_dma), 0, 0);
if (!dev->data_requests) {
DBG(dev, "can't get request data pool\n");
@@ -3105,7 +3105,7 @@ int init_dma_pools(struct udc *dev)
dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
/* dma desc for setup data */
- dev->stp_requests = dma_pool_create("setup requests", NULL,
+ dev->stp_requests = dma_pool_create("setup requests", dev->dev,
sizeof(struct udc_stp_dma), 0, 0);
if (!dev->stp_requests) {
DBG(dev, "can't get stp request pool\n");
--
2.1.0
^ permalink raw reply related
* [RFC v2 4/5] DT bindings documentation for Synopsys UDC platform driver
From: Raviteja Garimella @ 2017-01-17 8:05 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484640308-25976-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
This patch adds device tree bindings documentation for Synopsys
USB device controller platform driver.
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
.../devicetree/bindings/usb/snps,dw-ahb-udc.txt | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/snps,dw-ahb-udc.txt
diff --git a/Documentation/devicetree/bindings/usb/snps,dw-ahb-udc.txt b/Documentation/devicetree/bindings/usb/snps,dw-ahb-udc.txt
new file mode 100644
index 0000000..0c18327
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/snps,dw-ahb-udc.txt
@@ -0,0 +1,27 @@
+Synopsys USB Device controller.
+
+The device node is used for Synopsys Designware Cores AHB
+Subsystem Device Controller (UDC).
+
+This device node is used by UDCs integrated it Broadcom's
+Northstar2 and Cygnus SoC's.
+
+Required properties:
+ - compatible: should be "snps,dw-ahb-udc"
+ - reg: Offset and length of UDC register set
+ - interrupts: description of interrupt line
+ - phys: phandle to phy node.
+ - extcon: phandle to the extcon device. This is optional and
+ not required for those that don't require extcon support.
+ Extcon support will be required if the UDC is connected to
+ a Dual Role Device Phy that supports both Host and Device
+ mode based on the external cable.
+
+Example:
+ udc_dwc: usb@664e0000 {
+ compatible = "snps,dw-ahb-udc";
+ reg = <0x664e0000 0x2000>;
+ interrupts = <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>;
+ phys = <&usbdrd_phy>;
+ extcon = <&usbdrd_phy>";
+ };
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC v2 5/5] UDC: Add Synopsys UDC Platform driver
From: Raviteja Garimella @ 2017-01-17 8:05 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Felipe Balbi
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484640308-25976-1-git-send-email-raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
This patch adds platform driver support for Synopsys UDC.
A new driver file (snps_udc_plat.c) is created for this purpose,
where the platform driver registration is done based on the OF
node.
New members are added to the UDC data structure for having platform
device support along with extcon and phy support.
Kconfig and Makefile have the required changes to compile the same.
Signed-off-by: Raviteja Garimella <raviteja.garimella-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
drivers/usb/gadget/udc/Kconfig | 15 ++
drivers/usb/gadget/udc/Makefile | 1 +
drivers/usb/gadget/udc/amd5536udc.h | 14 ++
drivers/usb/gadget/udc/snps_udc_core.c | 54 ++++--
drivers/usb/gadget/udc/snps_udc_plat.c | 342 +++++++++++++++++++++++++++++++++
5 files changed, 407 insertions(+), 19 deletions(-)
create mode 100644 drivers/usb/gadget/udc/snps_udc_plat.c
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 9d889bb..44a2058 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -252,6 +252,21 @@ config USB_SNP_CORE
This IP is different to the High Speed OTG IP that can be enabled
by selecting USB_DWC2 or USB_DWC3 options.
+config USB_SNP_UDC_PLAT
+ tristate "Synopsys USB 2.0 Device controller"
+ select USB_SNP_CORE
+ select USB_GADGET_DUALSPEED
+ depends on (USB_GADGET && OF)
+ default ARCH_BCM_IPROC
+ help
+ This adds Platform Device support for Synopsys Designware core
+ AHB subsystem USB2.0 Device Controller (UDC).
+
+ This driver works with UDCs integrated into Broadcom's Northstar2
+ and Cygnus SoCs.
+
+ If unsure, say N.
+
#
# Controllers available in both integrated and discrete versions
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 3929f6f1..7451516 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
+obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
obj-$(CONFIG_USB_BDC_UDC) += bdc/
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index c252457..7884281 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -16,6 +16,7 @@
/* debug control */
/* #define UDC_VERBOSE */
+#include <linux/extcon.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -28,6 +29,9 @@
#define UDC_HSA0_REV 1
#define UDC_HSB1_REV 2
+/* Broadcom chip rev. */
+#define UDC_BCM_REV 10
+
/*
* SETUP usb commands
* needed, because some SETUP's are handled in hw, but must be passed to
@@ -112,6 +116,7 @@
#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000
#define UDC_DEVCTL_BRLEN_OFS 16
+#define UDC_DEVCTL_SRX_FLUSH 14
#define UDC_DEVCTL_CSR_DONE 13
#define UDC_DEVCTL_DEVNAK 12
#define UDC_DEVCTL_SD 10
@@ -564,7 +569,15 @@ struct udc {
u16 cur_intf;
u16 cur_alt;
+ /* for platform device and extcon support */
struct device *dev;
+ struct phy *udc_phy;
+ struct extcon_dev *edev;
+ struct extcon_specific_cable_nb extcon_nb;
+ struct notifier_block nb;
+ struct delayed_work drd_work;
+ struct workqueue_struct *drd_wq;
+ u32 conn_type;
};
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
@@ -580,6 +593,7 @@ int udc_enable_dev_setup_interrupts(struct udc *dev);
int udc_mask_unused_interrupts(struct udc *dev);
irqreturn_t udc_irq(int irq, void *pdev);
void gadget_release(struct device *pdev);
+void empty_req_queue(struct udc_ep *ep);
void udc_basic_init(struct udc *dev);
void free_dma_pools(struct udc *dev);
int init_dma_pools(struct udc *dev);
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 556fa0e..59e2d6a 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -34,7 +34,6 @@
#include "amd5536udc.h"
static void udc_tasklet_disconnect(unsigned long);
-static void empty_req_queue(struct udc_ep *);
static void udc_setup_endpoints(struct udc *dev);
static void udc_soft_reset(struct udc *dev);
static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
@@ -1241,7 +1240,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
}
/* Empty request queue of an endpoint; caller holds spinlock */
-static void empty_req_queue(struct udc_ep *ep)
+void empty_req_queue(struct udc_ep *ep)
{
struct udc_request *req;
@@ -1253,6 +1252,7 @@ static void empty_req_queue(struct udc_ep *ep)
complete_req(ep, req, -ESHUTDOWN);
}
}
+EXPORT_SYMBOL_GPL(empty_req_queue);
/* Dequeues a request packet, called by gadget driver */
static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
@@ -1620,6 +1620,9 @@ static void udc_setup_endpoints(struct udc *dev)
/* Bringup after Connect event, initial bringup to be ready for ep0 events */
static void usb_connect(struct udc *dev)
{
+ /* Return if already connected */
+ if (dev->connected)
+ return;
dev_info(dev->dev, "USB Connect\n");
@@ -1638,6 +1641,9 @@ static void usb_connect(struct udc *dev)
*/
static void usb_disconnect(struct udc *dev)
{
+ /* Return if already disconnected */
+ if (!dev->connected)
+ return;
dev_info(dev->dev, "USB Disconnect\n");
@@ -1712,11 +1718,15 @@ static void udc_soft_reset(struct udc *dev)
/* device int. status reset */
writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
- spin_lock_irqsave(&udc_irq_spinlock, flags);
- writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
- readl(&dev->regs->cfg);
- spin_unlock_irqrestore(&udc_irq_spinlock, flags);
-
+ /* Don't do this for Broadcom UDC since this is a reserved
+ * bit.
+ */
+ if (dev->chiprev != UDC_BCM_REV) {
+ spin_lock_irqsave(&udc_irq_spinlock, flags);
+ writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+ readl(&dev->regs->cfg);
+ spin_unlock_irqrestore(&udc_irq_spinlock, flags);
+ }
}
/* RDE timer callback to set RDE bit */
@@ -3168,21 +3178,27 @@ int udc_probe(struct udc *dev)
dev_info(dev->dev, "%s\n", mod_desc);
snprintf(tmp, sizeof(tmp), "%d", dev->irq);
- dev_info(dev->dev,
- "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
- tmp, dev->phys_addr, dev->chiprev,
- (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
- strcpy(tmp, UDC_DRIVER_VERSION_STRING);
- if (dev->chiprev == UDC_HSA0_REV) {
- dev_err(dev->dev, "chip revision is A0; too old\n");
- retval = -ENODEV;
- goto finished;
+
+ /* Print this device info for AMD chips only*/
+ if (dev->chiprev == UDC_HSA0_REV ||
+ dev->chiprev == UDC_HSB1_REV) {
+ dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
+ tmp, dev->phys_addr, dev->chiprev,
+ (dev->chiprev == UDC_HSA0_REV) ?
+ "A0" : "B1");
+ strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+ if (dev->chiprev == UDC_HSA0_REV) {
+ dev_err(dev->dev, "chip revision is A0; too old\n");
+ retval = -ENODEV;
+ goto finished;
+ }
+ dev_info(dev->dev,
+ "driver version: %s(for Geode5536 B1)\n", tmp);
}
- dev_info(dev->dev,
- "driver version: %s(for Geode5536 B1)\n", tmp);
+
udc = dev;
- retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
+ retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget,
gadget_release);
if (retval)
goto finished;
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
new file mode 100644
index 0000000..f5ee2f8
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -0,0 +1,342 @@
+/*
+ * snps_udc_plat.c - Synopsys UDC Platform Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/extcon.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/module.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include "amd5536udc.h"
+
+/* description */
+#define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
+
+void start_udc(struct udc *udc)
+{
+ if (udc->driver) {
+ dev_info(udc->dev, "Connecting...\n");
+ udc_enable_dev_setup_interrupts(udc);
+ udc_basic_init(udc);
+ udc->connected = 1;
+ }
+}
+
+void stop_udc(struct udc *udc)
+{
+ int tmp;
+ u32 reg;
+
+ spin_lock(&udc->lock);
+
+ /* Flush the receieve fifo */
+ reg = readl(&udc->regs->ctl);
+ reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
+ writel(reg, &udc->regs->ctl);
+
+ reg = readl(&udc->regs->ctl);
+ reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
+ writel(reg, &udc->regs->ctl);
+ dev_dbg(udc->dev, "ep rx queue flushed\n");
+
+ /* Mask interrupts. Required more so when the
+ * UDC is connected to a DRD phy.
+ */
+ udc_mask_unused_interrupts(udc);
+
+ /* Disconnect gadget driver */
+ if (udc->driver) {
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+
+ /* empty queues */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&udc->ep[tmp]);
+ }
+ udc->connected = 0;
+
+ spin_unlock(&udc->lock);
+ dev_info(udc->dev, "Device disconnected\n");
+}
+
+void udc_drd_work(struct work_struct *work)
+{
+ struct udc *udc;
+
+ udc = container_of(to_delayed_work(work),
+ struct udc, drd_work);
+
+ if (udc->conn_type) {
+ dev_dbg(udc->dev, "idle -> device\n");
+ start_udc(udc);
+ } else {
+ dev_dbg(udc->dev, "device -> idle\n");
+ stop_udc(udc);
+ }
+}
+
+static int usbd_connect_notify(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct udc *udc = container_of(self, struct udc, nb);
+
+ dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
+
+ udc->conn_type = event;
+
+ schedule_delayed_work(&udc->drd_work, 0);
+
+ return NOTIFY_OK;
+}
+
+static int udc_plat_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct udc *udc;
+ int ret;
+
+ udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ spin_lock_init(&udc->lock);
+ udc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ udc->virt_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(udc->regs))
+ return PTR_ERR(udc->regs);
+
+ /* udc csr registers base */
+ udc->csr = udc->virt_addr + UDC_CSR_ADDR;
+
+ /* dev registers base */
+ udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
+
+ /* ep registers base */
+ udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
+
+ /* fifo's base */
+ udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
+ udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
+
+ udc->phys_addr = (unsigned long)res->start;
+
+ udc->irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (udc->irq <= 0) {
+ dev_err(dev, "Can't parse and map interrupt\n");
+ return -EINVAL;
+ }
+
+ udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+ if (IS_ERR(udc->udc_phy)) {
+ dev_err(dev, "Failed to obtain phy from device tree\n");
+ return PTR_ERR(udc->udc_phy);
+ }
+
+ ret = phy_init(udc->udc_phy);
+ if (ret) {
+ dev_err(dev, "UDC phy init failed");
+ return ret;
+ }
+
+ ret = phy_power_on(udc->udc_phy);
+ if (ret) {
+ dev_err(dev, "UDC phy power on failed");
+ phy_exit(udc->udc_phy);
+ return ret;
+ }
+
+ /* Register for extcon if supported */
+ if (of_get_property(dev->of_node, "extcon", NULL)) {
+ udc->edev = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(udc->edev)) {
+ if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(dev, "Invalid or missing extcon\n");
+ ret = PTR_ERR(udc->edev);
+ goto exit_phy;
+ }
+
+ udc->nb.notifier_call = usbd_connect_notify;
+ ret = extcon_register_notifier(udc->edev, EXTCON_USB,
+ &udc->nb);
+ if (ret < 0) {
+ dev_err(dev, "Can't register extcon device\n");
+ goto exit_phy;
+ }
+
+ ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
+ if (ret < 0) {
+ dev_err(dev, "Can't get cable state\n");
+ goto exit_extcon;
+ } else if (ret) {
+ udc->conn_type = ret;
+ }
+ INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
+ }
+
+ /* init dma pools */
+ if (use_dma) {
+ ret = init_dma_pools(udc);
+ if (ret != 0)
+ goto exit_extcon;
+ }
+
+ ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
+ "snps-udc", udc);
+ if (ret < 0) {
+ dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
+ goto exit_dma;
+ }
+
+ platform_set_drvdata(pdev, udc);
+ udc->chiprev = UDC_BCM_REV;
+
+ if (udc_probe(udc)) {
+ ret = -ENODEV;
+ goto exit_dma;
+ }
+ dev_info(dev, "Synopsys UDC platform driver probe successful\n");
+
+ return 0;
+
+exit_dma:
+ if (use_dma)
+ free_dma_pools(udc);
+exit_extcon:
+ if (udc->edev)
+ extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
+exit_phy:
+ if (udc->udc_phy) {
+ phy_power_off(udc->udc_phy);
+ phy_exit(udc->udc_phy);
+ }
+ return ret;
+}
+
+static int udc_plat_remove(struct platform_device *pdev)
+{
+ struct udc *dev;
+
+ dev = platform_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&dev->gadget);
+ /* gadget driver must not be registered */
+ if (WARN_ON(dev->driver))
+ return 0;
+
+ /* dma pool cleanup */
+ free_dma_pools(dev);
+
+ udc_remove(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (dev->drd_wq) {
+ flush_workqueue(dev->drd_wq);
+ destroy_workqueue(dev->drd_wq);
+ }
+
+ phy_power_off(dev->udc_phy);
+ phy_exit(dev->udc_phy);
+ extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
+
+ dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int udc_plat_suspend(struct device *dev)
+{
+ struct udc *udc;
+
+ udc = dev_get_drvdata(dev);
+ usb_disconnect(udc);
+
+ if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+ dev_dbg(udc->dev, "device -> idle\n");
+ stop_udc(udc);
+ }
+ phy_power_off(udc->udc_phy);
+ phy_exit(udc->udc_phy);
+
+ return 0;
+}
+
+static int udc_plat_resume(struct device *dev)
+{
+ struct udc *udc;
+ int ret;
+
+ udc = dev_get_drvdata(dev);
+
+ ret = phy_init(udc->udc_phy);
+ if (ret) {
+ dev_err(udc->dev, "UDC phy init failure");
+ return ret;
+ }
+
+ ret = phy_power_on(udc->udc_phy);
+ if (ret) {
+ dev_err(udc->dev, "UDC phy power on failure");
+ phy_exit(udc->udc_phy);
+ return ret;
+ }
+
+ if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+ dev_dbg(udc->dev, "idle -> device\n");
+ start_udc(udc);
+ }
+
+ return 0;
+}
+static const struct dev_pm_ops udc_plat_pm_ops = {
+ .suspend = udc_plat_suspend,
+ .resume = udc_plat_resume,
+};
+#endif
+
+#if defined(CONFIG_OF)
+static const struct of_device_id of_udc_match[] = {
+ { .compatible = "snps,dw-ahb-udc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_udc_match);
+#endif
+
+static struct platform_driver udc_plat_driver = {
+ .probe = udc_plat_probe,
+ .remove = udc_plat_remove,
+ .driver = {
+ .name = "snps-udc-plat",
+ .of_match_table = of_match_ptr(of_udc_match),
+#ifdef CONFIG_PM_SLEEP
+ .pm = &udc_plat_pm_ops,
+#endif
+ },
+};
+module_platform_driver(udc_plat_driver);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH 1/4] phy: sun4i-usb: support PHY0 on H3 in MUSB mode
From: Maxime Ripard @ 2017-01-17 8:06 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Rob Herring, Chen-Yu Tsai, Kishon Vijay Abraham I,
Greg Kroah-Hartman, Bin Liu, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20170116191449.50397-2-icenowy-ymACFijhrKM@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 691 bytes --]
On Tue, Jan 17, 2017 at 03:14:46AM +0800, Icenowy Zheng wrote:
> The PHY0 on H3 can be wired either to MUSB controller or OHCI/EHCI
> controller.
>
> The original driver wired it to OHCI/EHCI controller; however, as the
> code to use PHY0 as OHCI/EHCI is missing, it makes the PHY fully
> unusable.
>
> Rename the register (according to its function and the name in BSP
> driver), and remove the code which wires the PHY0 to OHCI/EHCI, as MUSB
> can support both peripheral and host mode (although the host mode of
> MUSB is buggy).
Can you elaborate on that? What's wrong with it?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v4 5/5] i2c: mux: pca954x: Add irq_mask_en to delay enabling irqs
From: Phil Reid @ 2017-01-17 8:07 UTC (permalink / raw)
To: Peter Rosin, wsa-z923LK4zBo2bacvFa/9K2g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <b2e0ba39-2a1b-9c67-082d-d7d043ecc220-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 16/01/2017 20:08, Peter Rosin wrote:
>> @@ -280,9 +294,12 @@ static void pca954x_irq_unmask(struct irq_data *idata)
>>
>> spin_lock_irqsave(&data->lock, flags);
>>
>> - if (!data->irq_mask)
>> + if (!data->irq_mask_enable && !data->irq_mask)
>> enable_irq(data->client->irq);
>> data->irq_mask |= BIT(pos);
>> + if (data->irq_mask_enable &&
>> + (data->irq_mask & data->irq_mask) == data->irq_mask_enable)
>
> Hmm, I see that some apparently incompetent person :-) already acked this,
> but the (data->irq_mask & data->irq_mask) part doesn't make sense at all.
>
>> + enable_irq(data->client->irq);
>>
>
> Hmm2, if you have a problematic device (like the ltc1760) on mux segment 0
> and sane devices on other segments I'd be inclined to specify irq-mask-enable
> as 0x1. But then this is possible:
>
> 1. ltc1760 registers its irq
> 2. enable_irq(data->client->irq) is called because irq_mask_enable is "fulfilled"
> 3. a sane irq register an irq on some other segment
> 4. enable_irq(...) is called again (which the code appears to try to avoid)
>
> As I read the code, there will be problems with specifying irq-mask-enable
> whenever there are more than one irq-client on a mux segment.
>
> So, I'm removing my ack until the above is resolved...
>
G'day Peter,
Yes that is obviously wrong.
I think I've got it right this time in v5.
Also implemented your suggest on the array for the dt binding.
But stuck with the bitfield implementation for the moment.
Hopefully that's ok.
--
Regards
Phil Reid
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH 1/2] ARM: dts: dra7-evm: increase QSPI SPL partition size
From: B, Ravi @ 2017-01-17 8:14 UTC (permalink / raw)
To: Tony Lindgren
Cc: devicetree@vger.kernel.org, nsekhar@list.ti.com, Nori, Sekhar,
bcousson@baylibre.com, linux-omap@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20170113181239.GC2630@atomide.com>
Hi Tony
>* Ravi Babu <ravibabu@ti.com> [170113 04:41]:
>> The SPL size for DRA74x platform has increased and is now more than
>> 64KB. Increase QSPI SPL partition size to 256KB for DRA74x EVM.
>>
>> QSPI partition numbering changes because of this.
>And this will break the existing partitions potentially..
>See what was discussed on the list few days ago in thread "[PATCH 1/6] ARM: dts: am335x-phycore-som: Update NAND partition table".
>It's best to have these left empty or as they originally were and let u-boot configure the partitions.
Agree with you. For dra7xx platform the SPL size has been increased to 256KB and hence the existing QSPI SPL partition in kernel (64K size) will break when latest mainline u-boot is used.
Here only SPL partition has been changed and other partition & size is NOT changed and kept intact. I feel it will not break the existing partitions for dra7xx platform.
>> Signed-off-by: Ravi Babu <ravibabu@ti.com>
>> Signed-off-by: Sekhar Nori <nsekhar@ti.com>
>> ---
>> arch/arm/boot/dts/dra7-evm.dts | 24 ++++++------------------
>> 1 file changed, 6 insertions(+), 18 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/dra7-evm.dts
>> b/arch/arm/boot/dts/dra7-evm.dts index 132f2be..2784241 100644
>> --- a/arch/arm/boot/dts/dra7-evm.dts
>> +++ b/arch/arm/boot/dts/dra7-evm.dts
>> @@ -681,41 +681,29 @@
>> */
>> partition@0 {
>> label = "QSPI.SPL";
>> - reg = <0x00000000 0x000010000>;
>> + reg = <0x00000000 0x000040000>;
>> };
>> partition@1 {
>> - label = "QSPI.SPL.backup1";
>> - reg = <0x00010000 0x00010000>;
>> - };
>> - partition@2 {
>> - label = "QSPI.SPL.backup2";
>> - reg = <0x00020000 0x00010000>;
>> - };
>> - partition@3 {
>> - label = "QSPI.SPL.backup3";
>> - reg = <0x00030000 0x00010000>;
>> - };
>> - partition@4 {
>> label = "QSPI.u-boot";
>> reg = <0x00040000 0x00100000>;
>> };
>> - partition@5 {
>> + partition@2 {
>> label = "QSPI.u-boot-spl-os";
>> reg = <0x00140000 0x00080000>;
>> };
>> - partition@6 {
>> + partition@3 {
>> label = "QSPI.u-boot-env";
>> reg = <0x001c0000 0x00010000>;
>> };
>> - partition@7 {
>> + partition@4 {
>> label = "QSPI.u-boot-env.backup1";
>> reg = <0x001d0000 0x0010000>;
>> };
>> - partition@8 {
>> + partition@5 {
>> label = "QSPI.kernel";
>> reg = <0x001e0000 0x0800000>;
>> };
>> - partition@9 {
>> + partition@6 {
>> label = "QSPI.file-system";
>> reg = <0x009e0000 0x01620000>;
>> };
>> --
Regards
Ravi
^ permalink raw reply
* [PATCH v3 0/4] Add support for es8388 on the rock2
From: Romain Perier @ 2017-01-17 8:17 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, alsa-devel, Heiko Stuebner
Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell,
linux-rockchip, Sjoerd Simons, Rob Herring, Kumar Gala,
Romain Perier
This set of patches adds a machine driver for rockchip boards that use
ES8388 codecs. It also adds slave mode to the es8328 driver that
currently only supported the master mode. Then, it adds the required DT
definitions to link rockchip-i2s to the es8388 analog output.
This work is based on the initial work that was done by Sjoerd Simons
<sjoerd.simons@collabora.co.uk> with some improvements, changes and more
commits.
Changes in v3:
- Cosmetic changes in rockchip_es8388.c
- Added missing email to MODULE_AUTHOR in rockchip_es8388.c
Changes in v2:
- Fixed wrong use of the data structure i2c_device_id
- Fixed wrong dependencies for SND_SOC_ROCKCHIP_ES8388
Romain Perier (4):
ASoC: es8328-i2c: Add compatible for ES8388
ASoC: es8328: Add support for slave mode
ASoC: rockchip: Add machine driver for ES8388 codecs
arm: dts: Add support for ES8388 to the Radxa Rock 2
Documentation/devicetree/bindings/sound/es8328.txt | 2 +-
.../devicetree/bindings/sound/rockchip-es8388.txt | 28 +++
arch/arm/boot/dts/rk3288-rock2-square.dts | 37 +++
sound/soc/codecs/es8328-i2c.c | 2 +
sound/soc/codecs/es8328.c | 28 ++-
sound/soc/rockchip/Kconfig | 9 +
sound/soc/rockchip/Makefile | 2 +
sound/soc/rockchip/rockchip_es8388.c | 269 +++++++++++++++++++++
8 files changed, 370 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/rockchip-es8388.txt
create mode 100644 sound/soc/rockchip/rockchip_es8388.c
--
2.9.3
^ permalink raw reply
* [PATCH v3 1/4] ASoC: es8328-i2c: Add compatible for ES8388
From: Romain Perier @ 2017-01-17 8:17 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, alsa-devel, Heiko Stuebner
Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell,
linux-rockchip, Sjoerd Simons, Rob Herring, Kumar Gala,
Romain Perier
In-Reply-To: <20170117081705.14107-1-romain.perier@collabora.com>
This commit adds a compatible string for everest,es8388. This is
an audio codec that is compatible with es8328.
Signed-off-by: Romain Perier <romain.perier@collabora.com>
---
Changes in v3: None
Changes in v2:
- Fixed wrong use of the i2c_device_id data structure
Documentation/devicetree/bindings/sound/es8328.txt | 2 +-
sound/soc/codecs/es8328-i2c.c | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/sound/es8328.txt b/Documentation/devicetree/bindings/sound/es8328.txt
index 30ea8a3..33fbf05 100644
--- a/Documentation/devicetree/bindings/sound/es8328.txt
+++ b/Documentation/devicetree/bindings/sound/es8328.txt
@@ -4,7 +4,7 @@ This device supports both I2C and SPI.
Required properties:
- - compatible : "everest,es8328"
+ - compatible : Should be "everest,es8328" or "everest,es8388"
- DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V
- AVDD-supply : Regulator providing analog supply voltage 3.3V
- PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index 2d05b5d..318ab28 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -20,12 +20,14 @@
static const struct i2c_device_id es8328_id[] = {
{ "es8328", 0 },
+ { "es8388", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
static const struct of_device_id es8328_of_match[] = {
{ .compatible = "everest,es8328", },
+ { .compatible = "everest,es8388", },
{ }
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
--
2.9.3
^ permalink raw reply related
* [PATCH v3 2/4] ASoC: es8328: Add support for slave mode
From: Romain Perier @ 2017-01-17 8:17 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, alsa-devel, Heiko Stuebner
Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell,
linux-rockchip, Sjoerd Simons, Rob Herring, Kumar Gala,
Romain Perier
In-Reply-To: <20170117081705.14107-1-romain.perier@collabora.com>
Currently, the function that changes the DAI format only supports master
mode. Trying to use a slave mode exits the function with -EINVAL and
leave the codec misconfigured. This commits adds support for enabling
the slave mode.
Signed-off-by: Romain Perier <romain.perier@collabora.com>
---
Changes in v3: None
Changes in v2: None
sound/soc/codecs/es8328.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 37722194..054e123 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -588,10 +588,18 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
u8 dac_mode = 0;
u8 adc_mode = 0;
+ bool master;
- /* set master/slave audio interface */
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
- return -EINVAL;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -620,9 +628,17 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
- /* Master serial port mode, with BCLK generated automatically */
- snd_soc_update_bits(codec, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
+ if (master) {
+ /* Master serial port mode, with BCLK generated automatically */
+ snd_soc_update_bits(codec, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ ES8328_MASTERMODE_MSC);
+ } else {
+ /* Slave serial port mode */
+ snd_soc_update_bits(codec, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ 0);
+ }
return 0;
}
--
2.9.3
^ permalink raw reply related
* [PATCH v3 3/4] ASoC: rockchip: Add machine driver for ES8388 codecs
From: Romain Perier @ 2017-01-17 8:17 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, alsa-devel, Heiko Stuebner
Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell,
linux-rockchip, Sjoerd Simons, Rob Herring, Kumar Gala,
Romain Perier
In-Reply-To: <20170117081705.14107-1-romain.perier@collabora.com>
The driver is used for Rockchip boards using an audio codec compatible
with ES8388.
This commit is based on the initial work that was done by Sjoerd Simons
<sjoerd.simons@collabora.com> with some improvements.
Signed-off-by: Romain Perier <romain.perier@collabora.com>
---
Changes in v3:
- Cosmetic changes
- Added missing email to MODULE_AUTHOR
Changes in v2:
- Fixed wrong dependencies for SND_SOC_ROCKCHIP_ES8388, SND_SOC_ROCKCHIP_I2S
was selected with unmet direct dependencies
.../devicetree/bindings/sound/rockchip-es8388.txt | 28 +++
sound/soc/rockchip/Kconfig | 9 +
sound/soc/rockchip/Makefile | 2 +
sound/soc/rockchip/rockchip_es8388.c | 269 +++++++++++++++++++++
4 files changed, 308 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/rockchip-es8388.txt
create mode 100644 sound/soc/rockchip/rockchip_es8388.c
diff --git a/Documentation/devicetree/bindings/sound/rockchip-es8388.txt b/Documentation/devicetree/bindings/sound/rockchip-es8388.txt
new file mode 100644
index 0000000..316af2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/rockchip-es8388.txt
@@ -0,0 +1,28 @@
+ROCKCHIP with ES8388 CODECS
+
+Required properties:
+- compatible: "rockchip,rockchip-audio-es8388"
+- rockchip,model: The user-visible name of this sound complex
+- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's
+ connected to the CODEC
+- rockchip,audio-codec: The phandle of the es8388 audio codec
+
+Optionnal properties:
+- rockchip,hp-en-gpios = The phandle of the GPIO that power up/down the
+ headphone.
+- rockchip,hp-det-gpios = The phandle of the GPIO that detects the headphone
+- pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
+
+Example:
+
+sound {
+ compatible = "rockchip,rockchip-audio-es8388";
+ rockchip,model = "Analog audio output";
+ rockchip,i2s-controller = <&i2s>;
+ rockchip,audio-codec = <&es8388>;
+ rockchip,hp-en-gpios = <&gpio8 0 GPIO_ACTIVE_HIGH>;
+ rockchip,hp-det-gpios = <&gpio7 7 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&headphone>;
+};
+
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index c783f9a..b159744 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -42,6 +42,15 @@ config SND_SOC_ROCKCHIP_RT5645
Say Y or M here if you want to add support for SoC audio on Rockchip
boards using the RT5645/RT5650 codec, such as Veyron.
+config SND_SOC_ROCKCHIP_ES8388
+ tristate "ASoC support for Rockchip boards using a ES8388 codec"
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_ES8328_I2C
+ help
+ Say Y or M here if you want to add support for SoC audio on Rockchip
+ boards using the ES8388 Codec such as Radxa Rock 2
+
config SND_SOC_RK3399_GRU_SOUND
tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards"
depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP && SPI
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index 84e5c7c..759db5e 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -7,8 +7,10 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o
snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
+snd-soc-rockchip-es8388-objs := rockchip_es8388.o
snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_ES8388) += snd-soc-rockchip-es8388.o
obj-$(CONFIG_SND_SOC_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o
diff --git a/sound/soc/rockchip/rockchip_es8388.c b/sound/soc/rockchip/rockchip_es8388.c
new file mode 100644
index 0000000..723d025
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_es8388.c
@@ -0,0 +1,269 @@
+/*
+ * Rockchip machine ASoC driver for boards using a ES8388 CODEC.
+ *
+ * Copyright (c) 2016, Collabora Ltd.
+ *
+ * Authors: Sjoerd Simons <sjoerd.simons@collabora.com>,
+ * Romain Perier <romain.perier@collabora.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "rockchip_i2s.h"
+
+#define DRV_NAME "rockchip-snd-es8388"
+
+struct rk_es8388_drvdata {
+ int gpio_hp_en;
+ int gpio_hp_det;
+};
+
+static int rk_es8388_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct rk_es8388_drvdata *machine = snd_soc_card_get_drvdata(w->dapm->card);
+
+ if (!gpio_is_valid(machine->gpio_hp_en))
+ return 0;
+
+ gpio_set_value_cansleep(machine->gpio_hp_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static struct snd_soc_jack headphone_jack;
+static struct snd_soc_jack_pin headphone_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE
+ },
+};
+
+static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", rk_es8388_hp_power),
+};
+
+static const struct snd_soc_dapm_route rk_audio_map[] = {
+ {"Headphone", NULL, "LOUT2"},
+ {"Headphone", NULL, "ROUT2"},
+};
+
+static const struct snd_kcontrol_new rk_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+};
+
+static int rk_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int mclk;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_jack_gpio rk_hp_jack_gpio = {
+ .name = "Headphone detection",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150
+};
+
+static int rk_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct rk_es8388_drvdata *machine = snd_soc_card_get_drvdata(runtime->card);
+
+ /* Enable Headset Jack detection */
+ if (gpio_is_valid(machine->gpio_hp_det)) {
+ snd_soc_card_jack_new(runtime->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &headphone_jack,
+ headphone_jack_pins,
+ ARRAY_SIZE(headphone_jack_pins));
+ rk_hp_jack_gpio.gpio = machine->gpio_hp_det;
+ snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops rk_ops = {
+ .hw_params = rk_hw_params,
+};
+
+static struct snd_soc_dai_link rk_dailink = {
+ .name = "ES8388",
+ .stream_name = "Audio",
+ .codec_dai_name = "es8328-hifi-analog",
+ .init = rk_init,
+ .ops = &rk_ops,
+ /* Set es8388 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_card_rk = {
+ .name = "I2S-ES8388",
+ .dai_link = &rk_dailink,
+ .num_links = 1,
+ .num_aux_devs = 0,
+ .dapm_widgets = rk_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
+ .dapm_routes = rk_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
+ .controls = rk_mc_controls,
+ .num_controls = ARRAY_SIZE(rk_mc_controls),
+};
+
+static int snd_rk_mc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct snd_soc_card *card = &snd_soc_card_rk;
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_es8388_drvdata *machine;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct rk_es8388_drvdata),
+ GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card->dev = &pdev->dev;
+
+ machine->gpio_hp_det = of_get_named_gpio(np,
+ "rockchip,hp-det-gpios", 0);
+ if (machine->gpio_hp_det == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ machine->gpio_hp_en = of_get_named_gpio(np,
+ "rockchip,hp-en-gpios", 0);
+ if (machine->gpio_hp_en == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (gpio_is_valid(machine->gpio_hp_en)) {
+ ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en,
+ GPIOF_OUT_INIT_LOW, "hp_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get hp_en gpio\n");
+ return ret;
+ }
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "rockchip,model");
+ if (ret) {
+ dev_err(card->dev, "SoC parse card name failed %d\n", ret);
+ return ret;
+ }
+
+ rk_dailink.codec_of_node = of_parse_phandle(np, "rockchip,audio-codec",
+ 0);
+ if (!rk_dailink.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.cpu_of_node = of_parse_phandle(np, "rockchip,i2s-controller",
+ 0);
+ if (!rk_dailink.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,i2s-controller' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Soc register card failed %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static const struct of_device_id rockchip_es8388_of_match[] = {
+ { .compatible = "rockchip,rockchip-audio-es8388", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_es8388_of_match);
+
+static struct platform_driver snd_rk_es8388_driver = {
+ .probe = snd_rk_mc_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = rockchip_es8388_of_match,
+ },
+};
+
+module_platform_driver(snd_rk_es8388_driver);
+
+MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.com>");
+MODULE_DESCRIPTION("Rockchip es8388 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
--
2.9.3
^ permalink raw reply related
* [PATCH v3 4/4] arm: dts: Add support for ES8388 to the Radxa Rock 2
From: Romain Perier @ 2017-01-17 8:17 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, alsa-devel, Heiko Stuebner
Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell,
linux-rockchip, Sjoerd Simons, Rob Herring, Kumar Gala,
Romain Perier
In-Reply-To: <20170117081705.14107-1-romain.perier@collabora.com>
This commit adds the DT definition of the es8388 i2c device
found at address 0x10. It also adds the definition for connecting
the Rockchip I2S to the es8388 analog output.
This commit is based on the initial work that was done by Sjoerd Simons
<sjoerd.simons@collabora.com> with some improvements.
Signed-off-by: Romain Perier <romain.perier@collabora.com>
---
Changes in v3: None
Changes in v2: None
arch/arm/boot/dts/rk3288-rock2-square.dts | 37 +++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts
index dd3ad2e..82d20fd 100644
--- a/arch/arm/boot/dts/rk3288-rock2-square.dts
+++ b/arch/arm/boot/dts/rk3288-rock2-square.dts
@@ -86,6 +86,17 @@
#sound-dai-cells = <0>;
};
+ sound_es8388 {
+ compatible = "rockchip,rockchip-audio-es8388";
+ rockchip,model = "Analog audio output";
+ rockchip,i2s-controller = <&i2s>;
+ rockchip,audio-codec = <&es8388>;
+ rockchip,hp-en-gpios = <&gpio8 0 GPIO_ACTIVE_HIGH>;
+ rockchip,hp-det-gpios = <&gpio7 7 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&headphone>;
+ };
+
sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple";
clocks = <&hym8563>;
@@ -173,10 +184,29 @@
};
};
+&i2c2 {
+ status = "okay";
+
+ es8388: es8388@10 {
+ compatible = "everest,es8388", "everest,es8328";
+ reg = <0x10>;
+ AVDD-supply = <&vcca_codec>;
+ DVDD-supply = <&vcca_codec>;
+ HPVDD-supply = <&vcca_codec>;
+ PVDD-supply = <&vcca_codec>;
+ clocks = <&cru SCLK_I2S0_OUT>;
+ clock-names = "i2s_clk_out";
+ };
+};
+
&i2c5 {
status = "okay";
};
+&i2s {
+ status = "okay";
+};
+
&pinctrl {
ir {
ir_int: ir-int {
@@ -190,6 +220,13 @@
};
};
+ sound {
+ headphone: headphone {
+ rockchip,pins = <8 0 RK_FUNC_GPIO &pcfg_pull_up>,
+ <7 7 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
usb {
host_vbus_drv: host-vbus-drv {
rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>;
--
2.9.3
^ permalink raw reply related
* Re: [alsa-devel] [PATCH v2 3/4] ASoC: rockchip: Add machine driver for ES8388 codecs
From: Romain Perier @ 2017-01-17 8:18 UTC (permalink / raw)
To: Daniel Baluta
Cc: Mark Rutland, Devicetree List, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
Heiko Stuebner, Pawel Moll, Ian Campbell, Takashi Iwai,
Jaroslav Kysela, linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Sjoerd Simons, Rob Herring, Kumar Gala
In-Reply-To: <CAEnQRZDiwJpUJ+9UkGz0M9MjaTjCH6u5K-B2a_CzPr-zW8ET0g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Daniel,
Le 16/01/2017 à 11:27, Daniel Baluta a écrit :
> Two nits, see below.
All fixed,
Thanks,
Romain
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply
* Re: [PATCH v2 0/8] ARM: dts: Switch to new DSA binding
From: Gregory CLEMENT @ 2017-01-17 8:19 UTC (permalink / raw)
To: Florian Fainelli
Cc: linux-arm-kernel, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Rob Herring, Mark Rutland, Russell King,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, vivien.didelot
In-Reply-To: <61c15ec6-6c0d-7865-d140-dba4ca3657a4@gmail.com>
Hi Florian,
On mar., janv. 17 2017, Florian Fainelli <f.fainelli@gmail.com> wrote:
> On 01/05/2017 11:29 AM, Florian Fainelli wrote:
>> Hi all,
>>
>> This patch series converts the in-tree users to utilize the new (relatively)
>> DSA binding that was introduced with commit 8c5ad1d6179d ("net: dsa: Document
>> new binding"). The legacy binding node is kept included, but is marked
>> disabled.
>>
>> Changes in v2:
>>
>> - patch 1: Use an hexadecimal reg property
>> - patch 2: fixed the subject
>> - patch 3: s/okay/disabled/ for the legacy DSA node
>> - patches 7/8: fixed a stray whitespace
>>
>> In about 2-3 releases we may consider removing the old DSA binding entirely
>> from the kernel.
>
> Gregory, are you going to take this for 4.11? Thanks
it is my intend but I am waiting for the feedback from Andrew.
Gregory
>
>>
>> Thank you!
>>
>> Florian Fainelli (8):
>> ARM: dts: armada-370-rd: Utilize new DSA binding
>> ARM: dts: armada-385-linksys: Utilize new DSA binding
>> ARM: dts: armada-388-clearfog: Utilize new DSA binding
>> ARM: dts: armada-xp-linksys-mamba: Utilize new DSA binding
>> ARM: dts: kirkwood-dir665: Utilize new DSA binding
>> ARM: dts: kirkwood-linksys-viper: Utilize new DSA binding
>> ARM: dts: kirkwood-mv88f6281gtw-ge: Utilize new DSA binding
>> ARM: dts: kirkwood-rd88f6281: Utilize new DSA binding
>>
>> arch/arm/boot/dts/armada-370-rd.dts | 44 +++++++++++++++++
>> arch/arm/boot/dts/armada-385-linksys.dtsi | 52 ++++++++++++++++++++-
>> arch/arm/boot/dts/armada-388-clearfog.dts | 65 ++++++++++++++++++++++++++
>> arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 53 +++++++++++++++++++++
>> arch/arm/boot/dts/kirkwood-dir665.dts | 49 +++++++++++++++++++
>> arch/arm/boot/dts/kirkwood-linksys-viper.dts | 49 +++++++++++++++++++
>> arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts | 49 +++++++++++++++++++
>> arch/arm/boot/dts/kirkwood-rd88f6281-z0.dts | 11 +++++
>> arch/arm/boot/dts/kirkwood-rd88f6281.dtsi | 44 +++++++++++++++++
>> 9 files changed, 415 insertions(+), 1 deletion(-)
>>
>
>
> --
> Florian
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH] ARM: dts: r7s72100: fix sdhi clock define
From: Wolfram Sang @ 2017-01-17 8:21 UTC (permalink / raw)
To: Chris Brandt
Cc: Geert Uytterhoeven, Simon Horman, Magnus Damm, Rob Herring,
Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Linux-Renesas, Wolfram Sang
In-Reply-To: <SG2PR06MB11656A823E53F55C4FBCA7E88A7C0-ESzmfEwOt/xoAsOJh7vwSm0DtJ1/0DrXvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 604 bytes --]
> The reason is that would then keep me from having to modify the existing
> functions sh_mobile_sdhi_clk_enable/disable.
Why do you prefer this? I may be missing something but a small if-block
per function are not expensive IMO.
> Is anyone going to have an issue if I turn the card-detect clock on but never
> turn it off???? That was the patch that I was going to test out and submit.
It smells a bit hacky to me. And while hacky things are sometimes
needed, IMO this doesn't hold true here. With just 3 more lines (an
if-block in disable and a variable for the new clock) we can have it
proper.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v4] ARM64: dts: meson-gx: Add reserved memory zone and usable memory range
From: Neil Armstrong @ 2017-01-17 8:21 UTC (permalink / raw)
To: Olof Johansson
Cc: Andreas Färber, Kevin Hilman, devicetree@vger.kernel.org,
xypron.glpk, linux-kernel@vger.kernel.org, Carlo Caione,
linux-amlogic, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAOesGMhYfhrd-hy=N-FYDEPmumR0=QNzn5xTLxFuKG7apo5qiw@mail.gmail.com>
On 01/17/2017 07:07 AM, Olof Johansson wrote:
> On Mon, Jan 16, 2017 at 2:39 AM, Neil Armstrong <narmstrong@baylibre.com> wrote:
>> On 01/15/2017 03:43 PM, Andreas Färber wrote:
>>> Am 13.01.2017 um 21:03 schrieb Kevin Hilman:
>>>> Neil Armstrong <narmstrong@baylibre.com> writes:
>>>>
>>>>> The Amlogic Meson GXBB/GXL/GXM secure monitor uses part of the memory space,
>>>>> this patch adds this reserved zone and redefines the usable memory range.
>>>>>
>>>>> The memory node is also moved from the dtsi files into the proper dts files
>>>>> to handle variants memory sizes.
>>>>>
>>>>> This patch also fixes the memory sizes for the following platforms :
>>>>> - gxl-s905x-p212 : 1GiB instead of 2GiB, a proper 2GiB dts should be pushed
>>>>> - gxm-s912-q201 : 1GiB instead of 2GiB, a proper 2GiB dts should be pushed
>>>>> - gxl-s905d-p231 : 1GiB instead of 2GiB, a proper 2GiB dts should be pushed
>>>>> - gxl-nexbox-a95x : 1GiB instead of 2GiB, a proper 2GiB dts should be pushed
>>>>>
>>>>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>
>>>> Queued for v4.10-rc.
>>>
>>> What is the motivation for this change? I have a local U-Boot patch to
>>> detect the amount of memory available as done downstream, but U-Boot
>>> only updates the reg property that you seem to be abandoning here...
>>>
>>> So for devices that come in multiple RAM configurations - like R-Box Pro
>>> - this would require separate .dts files now! This looks very wrong to
>>> me, especially since I am not aware of other platforms doing the same.
>>> Instead, there's memory reservations for top and bottom done in U-Boot
>>> for reg, plus reserved-memory nodes for anything in the middle.
>>>
>>> Another thing to consider is that uEFI boot (bootefi) handles memory
>>> reservation differently yet again, on the bootloader level. I have had
>>> that working fine on Odroid-C2 and Vega S95.
>>>
>>> So if there's no bug this is fixing (none mentioned in commit message) I
>>> strongly object to this patch.
>>>
>>> Regards,
>>> Andreas
>>>
>>
>> Hi Andreas,
>>
>> Like I replied of my RFT patch :
>> I really disagree about relying on any work or properties added by any bootloader here, Amlogic SoCs has
>> a lot of u-boot versions in the field, and the Odroid-C2 is part of this.
>>
>> Even if Odroid-c2 is in mainline U-Boot or not, the mainline Linux kernel should work using
>> any U-boot version even with the one provided by Amlogic on their openlinux distribution channel.
>>
>> Handling multiple RAM configuration is another story, and the Arm-Soc and DT maintainers should give us
>> their advices.
>
> Is there a way to detect what firmware is running and marking off
> memory from early kernel init instead? That'll take care of the
> concerns about memory size variance as well.
>
>> Actually there is a severe bug fixed here that cause a huge crash if such memory is not reserved while
>> running stock u-boot version on various shipped products and Amlogic's own development boards.
>>
>> The bug is easily triggered by running :
>> # stress --vm 4 --vm-bytes 128M --timeout 10s &
>> [ 46.937975] Bad mode in Error handler detected on CPU1, code 0xbf000000 -- SError
>> ...
>> [ 47.058536] Internal error: Attempting to execute userspace memory: 8600000f [#3] PREEMPT SMP
>> ...
>>
>> Note this is a fix targeted for 4.10 to make the system stable and various users reported some severe
>> crash now the system has more drivers and read-world use-cases are running on Amlogic SoCs.
>>
>> Please feel free to push whatever changes that makes this memory reservation more coherent for 4.11,
>> and respect the behavior of already shipped u-boot version and mainline U-Boot, UEFI, whatever...
>
> Technically we're not in regression territory here, since the platform
> is obviously still in bringup and these aren't bugs that have been
> introduced in this release. So I think we can take a little while to
> sort out if there's a solution that, even if not ideal, at least is on
> the path towards the proper fix and not away from it -- which this
> seems to be.
>
>
> -Olof
>
Hi Olof, Andreas,
As I finally understand, the real issue here is the usage of the "linux,useable-memory" property that
overrides the reg property that is changed by the bootloader to provide the "real" memory size.
As I understand the mainline U-Boot does it right, and it's a good news, and it seems uEFI need to provide
some specialized memory range aswell, but the vendor U-Boot versions only provide the full memory range here.
It seems obvious that whatever range is provided by u-boot, the first 16MiB should be reserved.
The stress-ng package provides this "stress" command and is used to force the kernel to map more memory
zones, but I also got the issue while running a fully fledged Desktop Environment thanks to the
recently merged DRM driver.
You may not be able to trigger the issue since it seems Amlogic reduces this reserved size on GXL/GXM :
https://github.com/khadas/linux/commit/698df2c6cfbb0d1a9359743208e83517b31da6ce
But it should be confirmed.
Kevin asked me initially to handle this "start of ddr" reserved zone via a reserved-memory entry, but
at that time it seemed a better idea to use "linux,useable-memory", but I recon it may be an error.
I will push a v5 with a supplementary reserved-memory entry and will postpone the boards memory size
fixup for a future DTS cleanup.
Andreas, is this ok for you ?
This issue exists since forever on mainline linux, and even 4.9 has it.
Olof, How could a similar fix go in 4.9 stable ?
Thanks,
Neil
^ permalink raw reply
* Re: [PATCH v1] ARM: dts: imx: add fsl, imx35-wdt compatible to all relevant watchdog nodes
From: Uwe Kleine-König @ 2017-01-17 8:21 UTC (permalink / raw)
To: Vladimir Zapolskiy
Cc: Shawn Guo, Fabio Estevam, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Russell King, Stefan Agner,
Wim Van Sebroeck,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sascha Hauer,
Guenter Roeck
In-Reply-To: <20170117012336.6891-1-vz-ChpfBGZJDbMAvxtiuMwx3w@public.gmane.org>
On Tue, Jan 17, 2017 at 03:23:36AM +0200, Vladimir Zapolskiy wrote:
> Watchdog device controller found on all modern SoCs from i.MX series
> and firstly introduced in i.MX35 is not one in one compatible with the
> watchdog controllers on i.MX21, i.MX27 and i.MX31, the latter
> controlles don't have WICR (and pretimeout notification support) and
> WMCR registers. To get benefit from the more advanced watchdog device
> and to avoid operations over non-existing registers on legacy SoCs add
> fsl,imx35-wdt compatible to descriptions of all i.MX35 compatible
> watchdog controllers.
>
> Signed-off-by: Vladimir Zapolskiy <vz-ChpfBGZJDbMAvxtiuMwx3w@public.gmane.org>
> ---
> RFC change is found at https://patchwork.kernel.org/patch/9350007
>
> Changes from RFC to v1:
> * added the same change to devicetree bindings documentation, thanks
> to Baruch Siach for review,
> * replaced a new shared compatible derived from i.MX25 with an earlier
> from i.MX35 SoC one, thanks to Uwe Kleine-König for review.
>
> Compatible "fsl,imx21-wdt" is preserved as a generic one, because
> interface to a watchdog controller on i.MX35 is a superset of the
> interface to a i.MX21 watchdog controller.
One could argue here, but apart from the discussion if this is correct
or not, choosing this way has the upside that teaching the driver after
this change about the i.MX35 type doesn't make the machines based on the
newer type suffer from a breakage for one commit.
> Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt | 2 +-
When I created my patch set for this breakage I wondered if the
documentation should point out which are the unique types.
> arch/arm/boot/dts/imx25.dtsi | 3 ++-
> arch/arm/boot/dts/imx50.dtsi | 3 ++-
> arch/arm/boot/dts/imx51.dtsi | 6 ++++--
> arch/arm/boot/dts/imx53.dtsi | 6 ++++--
> arch/arm/boot/dts/imx6qdl.dtsi | 6 ++++--
> arch/arm/boot/dts/imx6sl.dtsi | 6 ++++--
> arch/arm/boot/dts/imx6sx.dtsi | 9 ++++++---
> arch/arm/boot/dts/imx6ul.dtsi | 6 ++++--
> arch/arm/boot/dts/imx7s.dtsi | 12 ++++++++----
> arch/arm/boot/dts/ls1021a.dtsi | 2 +-
> arch/arm/boot/dts/vfxxx.dtsi | 3 ++-
The imx21 type is also used in
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
What about these?
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v29 9/9] Documentation: dt: chosen properties for arm64 kdump
From: Dave Young @ 2017-01-17 8:26 UTC (permalink / raw)
To: AKASHI Takahiro, Mark Rutland, catalin.marinas, will.deacon,
robh+dt, james.morse, geoff, bauerman, kexec, linux-arm-kernel,
devicetree
In-Reply-To: <20170116082505.GL20972@linaro.org>
On 01/16/17 at 05:25pm, AKASHI Takahiro wrote:
> On Fri, Jan 13, 2017 at 11:17:56AM +0000, Mark Rutland wrote:
> > On Fri, Jan 13, 2017 at 06:13:49PM +0900, AKASHI Takahiro wrote:
> > > On Thu, Jan 12, 2017 at 03:39:45PM +0000, Mark Rutland wrote:
> > > > On Wed, Dec 28, 2016 at 01:37:34PM +0900, AKASHI Takahiro wrote:
> > > > > +linux,crashkernel-base
> > > > > +linux,crashkernel-size
> > > > > +----------------------
> > > > > +
> > > > > +These properties (currently used on PowerPC and arm64) indicates
> > > > > +the base address and the size, respectively, of the reserved memory
> > > > > +range for crash dump kernel.
> > > >
> > > > From this description, it's not clear to me what the (expected)
> > > > consumers of this property are, nor what is expected to provide it.
> > > >
> > > > In previous rounds of review, I had assumed that this was used to
> > > > describe a preference to the first kernel as to what region of memory
> > > > should be used for a subsequent kdump kernel. Looking around, I'm not
> > > > sure if I was correct in that assessment.
> > > >
> > > > I see that arch/powerpc seems to consume this property to configure
> > > > crashk_res, but it also rewrites it based on crashk_res, presumably for
> > > > the benefit of userspace. It's not clear to me how on powerpc the kdump
> > > > kernel knows its memory range -- is more DT modification done in the
> > > > kernel and/or userspace?
> > >
> > > I don't believe that powerpc will rewrite the property any way.
> > > As far as I know from *the source code*, powerpc kernel retrieves
> > > the memory range for crash dump kernel from a kernel command line, i.e.
> > > crashkernel=, and then exposes it through DT to userspace (assuming
> > > kexec-tools).
> >
> > The rewriting I describe is in export_crashk_values() in
> > arch/powerpc/kernel/machine_kexec.c, where the code deletes existing the
> > properties, and adds new ones, to the DT exposed to userspace.
> >
> > So I think we're just quibbling over the definition of "rewrite".
>
> Gotcha
>
> > > > arm64 we should either ensure that /proc/iomem is consistently usable
> > > > (and have userspace consistently use it), or we should expose a new file
> > > > specifically to expose this information.
> > >
> > > The thing that I had in my mind when adding this property is that
> > > /proc/iomem would be obsolete in the future, then we should have
> > > an alternative in hand.
> >
> > Ok.
> >
> > My disagreement is with using the DT as a channel to convey information
> > from the kernel to userspace.
> >
> > I'm more than happy for a new file or other mechanism to express this
> > information. For example, we could add
> > /sys/kernel/kexec_crash_{base,size} or similar.
>
> It may make sense because /sys/kernel/kexec_crash_size already exists,
> so why not kexec_crash_base?
> My concern, however, is that this kind of interface might prevent us from
> allowing multiple regions to be reserved for crash dump kernel in the future.
> (There is an assumption that we have only one region at least on arm64 though.)
In x86 there could be two ranges, one for softiotlb under 4G and another
for range over 4G, but kexec_crash_size only shows the size of
over-4g-range.
It is better to use /proc/iomem, most arches use /proc/iomem. Do you
have any reason why it will be obsolete? At least for the time being it
is fine.
>
> Thanks,
> -Takahiro AKASHI
>
> >
> > > > Further, I do not think we need this property. It makes more sense to me
> > > > for the preference of a a region to be described to the *first* kernel
> > > > using the command line consistently.
> > > >
> > > > So I think we should drop this property, and not use it on arm64. Please
> > > > document this as powerpc only.
> > >
> > > OK, but if we drop the property from arm64 code, we have no reason
> > > to leave its description in this patch.
> > > (In fact, there are a few more (undocumented) properties that only ppc
> > > uses for kdump.)
> >
> > I'm happy to drop it, then.
> >
> > > > > +linux,usable-memory-range
> > > > > +-------------------------
> > > > > +
> > > > > +This property (currently used only on arm64) holds the memory range,
> > > > > +the base address and the size, which can be used as system ram on
> > > > > +the *current* kernel. Note that, if this property is present, any memory
> > > > > +regions under "memory" nodes in DT blob or ones marked as "conventional
> > > > > +memory" in EFI memory map should be ignored.
> > > >
> > > > Could you please replace this with:
> > > >
> > > > This property (arm64 only) holds a base address and size, describing a
> > > > limited region in which memory may be considered available for use by
> > > > the kernel. Memory outside of this range is not available for use.
> > > >
> > > > This property describes a limitation: memory within this range is only
> > > > valid when also described through another mechanism that the kernel
> > > > would otherwise use to determine available memory (e.g. memory nodes
> > > > or the EFI memory map). Valid memory may be sparse within the range.
> > >
> > > Sure.
> >
> > Cheers!
> >
> > Thanks,
> > Mark.
>
> _______________________________________________
> kexec mailing list
> kexec@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply
* Re: Dropping device tree pinmux nodes for GPIO usage (Was: [PATCH 3/5] arm64: dts: sun50i: add MMC nodes)
From: Maxime Ripard @ 2017-01-17 8:33 UTC (permalink / raw)
To: Rask Ingemann Lambertsen
Cc: Chen-Yu Tsai, Andre Przywara, Mark Rutland, devicetree,
Ulf Hansson, linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-sunxi, linux-kernel, Hans De Goede, Rob Herring,
Icenowy Zheng, linux-arm-kernel
In-Reply-To: <20170115155931.l5tjcdhbz6sj3wke@localhost>
[-- Attachment #1: Type: text/plain, Size: 925 bytes --]
On Sun, Jan 15, 2017 at 04:59:32PM +0100, Rask Ingemann Lambertsen wrote:
> On Tue, Jan 03, 2017 at 10:52:12AM +0800, Chen-Yu Tsai wrote:
> > On Tue, Jan 3, 2017 at 7:03 AM, Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org> wrote:
> > > +
> > > + mmc0_default_cd_pin: mmc0_cd_pin@0 {
> > > + pins = "PF6";
> > > + function = "gpio_in";
> > > + bias-pull-up;
> > > + };
> >
> > We are starting to drop pinmux nodes for gpio usage.
>
> How do we get the equivalent of bias-pull-up/down and drive-strength if we
> run across a pin that needs it?
Hmm, that's actually a very good question...
For those cases, leave the pinctrl node for now, we'll deal with that
in due time
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v3 1/3] dt: bindings: add documentation for zx2967 family reset controller
From: Philipp Zabel @ 2017-01-17 8:42 UTC (permalink / raw)
To: Baoyou Xie
Cc: mark.rutland, devicetree, xie.baoyou, linux-kernel, robh+dt,
chen.chaokai, wang.qiang01, jun.nie, shawnguo, linux-arm-kernel
In-Reply-To: <1484623377-16208-1-git-send-email-baoyou.xie@linaro.org>
On Tue, 2017-01-17 at 11:22 +0800, Baoyou Xie wrote:
> This patch adds dt-binding documentation for zx2967 family
> reset controller.
>
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> Reviewed-by: Shawn Guo <shawnguo@kernel.org>
Thanks, I've applied all three patches with Shawn's Reviewed-by: on the
driver patch.
regards
Philipp
> ---
> .../devicetree/bindings/reset/zte,zx2967-reset.txt | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt
>
> diff --git a/Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt b/Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt
> new file mode 100644
> index 0000000..b015508
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt
> @@ -0,0 +1,20 @@
> +ZTE zx2967 SoCs Reset Controller
> +=======================================
> +
> +Please also refer to reset.txt in this directory for common reset
> +controller binding usage.
> +
> +Required properties:
> +- compatible: should be one of the following.
> + * zte,zx296718-reset
> +- reg: physical base address of the controller and length of memory mapped
> + region.
> +- #reset-cells: must be 1.
> +
> +example:
> +
> + reset: reset-controller@1461060 {
> + compatible = "zte,zx296718-reset";
> + reg = <0x01461060 0x8>;
> + #reset-cells = <1>;
> + };
^ permalink raw reply
* Re: [PATCH v1 3/3] watchdog: zx2967: add watchdog controller driver for ZTE's zx2967 family
From: Shawn Guo @ 2017-01-17 8:55 UTC (permalink / raw)
To: Baoyou Xie
Cc: mark.rutland, devicetree, linux-watchdog, xie.baoyou,
linux-kernel, wim, robh+dt, chen.chaokai, linux-arm-kernel,
wang.qiang01, jun.nie, linux
In-Reply-To: <1484540395-3335-3-git-send-email-baoyou.xie@linaro.org>
On Mon, Jan 16, 2017 at 12:19:55PM +0800, Baoyou Xie wrote:
> This patch adds watchdog controller driver for ZTE's zx2967 family.
>
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> ---
> drivers/watchdog/Kconfig | 10 ++
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/zx2967_wdt.c | 405 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 416 insertions(+)
> create mode 100644 drivers/watchdog/zx2967_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 3eb58cb..79027da 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -714,6 +714,16 @@ config ASPEED_WATCHDOG
> To compile this driver as a module, choose M here: the
> module will be called aspeed_wdt.
>
> +config ZX2967_WATCHDOG
> + tristate "ZTE zx2967 SoCs watchdog support"
> + depends on ARCH_ZX
> + select WATCHDOG_CORE
> + help
> + Say Y here to include support for the watchdog timer
> + in ZTE zx2967 SoCs.
> + To compile this driver as a module, choose M here: the
> + module will be called zx2967_wdt.
> +
> # AVR32 Architecture
>
> config AT32AP700X_WDT
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index caa9f4a..ea08925 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -82,6 +82,7 @@ obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
> obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
> obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
> obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
> +obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
>
> # AVR32 Architecture
> obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
> diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c
> new file mode 100644
> index 0000000..8791dd2
> --- /dev/null
> +++ b/drivers/watchdog/zx2967_wdt.c
> @@ -0,0 +1,405 @@
> +/*
> + * watchdog driver for ZTE's zx2967 family
> + *
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie@linaro.org>
> + *
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset.h>
> +#include <linux/watchdog.h>
> +
> +#define ZX2967_WDT_CFG_REG 0x4
> +#define ZX2967_WDT_LOAD_REG 0x8
> +#define ZX2967_WDT_REFRESH_REG 0x18
> +#define ZX2967_WDT_START_REG 0x1c
> +
> +#define ZX2967_WDT_REFRESH_MASK 0x3f
> +
> +#define ZX2967_WDT_CFG_DIV(n) ((((n)&0xff) - 1) << 8)
Please have a space before and after '&' operator.
> +#define ZX2967_WDT_START_EN 0x1
> +
> +#define ZX2967_WDT_WRITEKEY 0x12340000
> +
> +#define ZX2967_WDT_DIV_DEFAULT 16
> +#define ZX2967_WDT_DEFAULT_TIMEOUT 32
> +#define ZX2967_WDT_MIN_TIMEOUT 1
> +#define ZX2967_WDT_MAX_TIMEOUT 500
> +#define ZX2967_WDT_MAX_COUNT 0xffff
> +
> +#define ZX2967_WDT_FLAG_REBOOT_MON (1 << 0)
> +
> +#define ZX2967_RESET_MASK_REG 0xb0
> +
> +#define zx2967_wdt_write_reg(v, r) \
> + writel((v) | ZX2967_WDT_WRITEKEY, r)
> +#define zx2967_wdt_read_reg(r) readl(r)
I think inline functions are better than macros in this case.
> +
> +static bool nowayout = WATCHDOG_NOWAYOUT;
> +static unsigned int timeout = ZX2967_WDT_DEFAULT_TIMEOUT;
The variable name is too generic. For example, the same name is used in
functions like __zx2967_wdt_set_timeout().
> +
> +struct zx2967_wdt {
> + struct device *dev;
> + struct clk *clock;
> + void __iomem *reg_base;
> + unsigned int conf;
> + unsigned int load;
> + unsigned int flags;
> + spinlock_t lock;
> + struct watchdog_device wdt_device;
> + struct notifier_block restart_handler;
> + struct notifier_block reboot_handler;
> +};
> +
> +static void zx2967_wdt_refresh(struct zx2967_wdt *wdt)
> +{
> + unsigned int val;
u32 for 32-bit register access is more readable.
> +
> + spin_lock(&wdt->lock);
> +
> + val = zx2967_wdt_read_reg(wdt->reg_base + ZX2967_WDT_REFRESH_REG);
> + val ^= ZX2967_WDT_REFRESH_MASK;
> + zx2967_wdt_write_reg(val, wdt->reg_base + ZX2967_WDT_REFRESH_REG);
> +
> + spin_unlock(&wdt->lock);
> +}
> +
> +static void __zx2967_wdt_stop(struct zx2967_wdt *wdt)
> +{
> + unsigned int val;
> +
> + spin_lock(&wdt->lock);
> +
> + val = zx2967_wdt_read_reg(wdt->reg_base + ZX2967_WDT_START_REG);
> + val &= ~(ZX2967_WDT_START_EN);
The parentheses is not needed.
> + zx2967_wdt_write_reg(val, wdt->reg_base + ZX2967_WDT_START_REG);
> +
> + spin_unlock(&wdt->lock);
> +}
> +
> +static void __zx2967_wdt_start(struct zx2967_wdt *wdt)
> +{
> + unsigned int val;
> +
> + spin_lock(&wdt->lock);
> +
> + val = zx2967_wdt_read_reg(wdt->reg_base + ZX2967_WDT_START_REG);
> + val |= ZX2967_WDT_START_EN;
> + zx2967_wdt_write_reg(val, wdt->reg_base + ZX2967_WDT_START_REG);
> +
> + spin_unlock(&wdt->lock);
> +}
> +
> +static unsigned int
> +__zx2967_wdt_set_timeout(struct zx2967_wdt *wdt, unsigned int timeout)
> +{
> + unsigned int freq = clk_get_rate(wdt->clock);
> + unsigned int divisor = ZX2967_WDT_DIV_DEFAULT, count;
Unless the variables are closely related, it's more conventional to have
each variable on a new line.
> +
> + count = timeout * freq;
> + if (count > divisor * ZX2967_WDT_MAX_COUNT)
> + divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT);
> + count = DIV_ROUND_UP(count, divisor);
> + zx2967_wdt_write_reg(ZX2967_WDT_CFG_DIV(divisor),
> + wdt->reg_base + ZX2967_WDT_CFG_REG);
> + zx2967_wdt_write_reg(count, wdt->reg_base + ZX2967_WDT_LOAD_REG);
> + zx2967_wdt_refresh(wdt);
> +
> + wdt->load = count;
> + dev_info(wdt->dev, "count=%d, timeout=%d, divisor=%d\n",
> + count, timeout, divisor);
Noisy. Maybe dev_dbg().
> +
> + return (count * divisor) / freq;
> +}
> +
> +static int zx2967_wdt_set_timeout(struct watchdog_device *wdd,
> + unsigned int timeout)
> +{
> + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + if (watchdog_timeout_invalid(&wdt->wdt_device, timeout)) {
> + dev_err(wdt->dev, "timeout %d is invalid\n", timeout);
> +
Drop the newline.
> + return -EINVAL;
> + }
> +
> + wdd->timeout = __zx2967_wdt_set_timeout(wdt, timeout);
> +
> + return 0;
> +}
> +
> +static int zx2967_wdt_start(struct watchdog_device *wdd)
> +{
> + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + __zx2967_wdt_stop(wdt);
> + zx2967_wdt_set_timeout(wdd, wdd->timeout);
> + __zx2967_wdt_start(wdt);
> +
> + return 0;
> +}
> +
> +static int zx2967_wdt_stop(struct watchdog_device *wdd)
> +{
> + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + __zx2967_wdt_stop(wdt);
> +
> + return 0;
> +}
> +
> +static int zx2967_wdt_keepalive(struct watchdog_device *wdd)
> +{
> + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + zx2967_wdt_refresh(wdt);
> +
> + return 0;
> +}
> +
> +#define ZX2967_WDT_OPTIONS \
> + (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
> +static const struct watchdog_info zx2967_wdt_ident = {
> + .options = ZX2967_WDT_OPTIONS,
> + .firmware_version = 0,
> + .identity = "zx2967 watchdog",
> +};
> +
> +static struct watchdog_ops zx2967_wdt_ops = {
> + .owner = THIS_MODULE,
> + .start = zx2967_wdt_start,
> + .stop = zx2967_wdt_stop,
> + .ping = zx2967_wdt_keepalive,
> + .set_timeout = zx2967_wdt_set_timeout,
> +};
> +
> +static void zx2967_wdt_fix_sysdown(struct zx2967_wdt *wdt)
> +{
> + __zx2967_wdt_stop(wdt);
> + __zx2967_wdt_set_timeout(wdt, 15);
> + __zx2967_wdt_start(wdt);
> +}
> +
> +static int zx2967_wdt_notify_sys(struct notifier_block *this,
> + unsigned long code, void *unused)
> +{
> + struct zx2967_wdt *wdt = container_of(this, struct zx2967_wdt,
> + reboot_handler);
> +
> + wdt->flags |= ZX2967_WDT_FLAG_REBOOT_MON;
> + switch (code) {
> + case SYS_HALT:
> + case SYS_POWER_OFF:
> + case SYS_RESTART:
> + zx2967_wdt_fix_sysdown(wdt);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int zx2967_wdt_restart(struct notifier_block *this,
> + unsigned long mode, void *cmd)
> +{
> + struct zx2967_wdt *wdt;
> +
> + wdt = container_of(this, struct zx2967_wdt, restart_handler);
> +
> + zx2967_wdt_stop(&wdt->wdt_device);
> +
> + zx2967_wdt_write_reg(0x80, wdt->reg_base + ZX2967_WDT_LOAD_REG);
> + zx2967_wdt_refresh(wdt);
> + zx2967_wdt_write_reg(ZX2967_WDT_START_EN,
> + wdt->reg_base + ZX2967_WDT_START_REG);
> +
> + zx2967_wdt_start(&wdt->wdt_device);
> + /* wait for reset*/
> + mdelay(500);
> +
> + return NOTIFY_DONE;
> +}
> +
> +static void zx2967_reset_mask_config(struct device *dev)
> +{
> + struct device_node *np = NULL;
> + void __iomem *reg;
> + unsigned int val, mask, config, size;
> + const unsigned int *prop;
> +
> + prop = of_get_property(dev->of_node, "reset-mask-config", &size);
> + if (size < (sizeof(*prop) * 2)) {
> + dev_err(dev, "bad data for reset-mask-config");
> + return;
> + }
> + config = be32_to_cpup(prop++);
> + mask = be32_to_cpup(prop);
> + np = of_find_compatible_node(NULL, NULL, "zte,aon-sysctrl");
> + if (!np) {
> + dev_err(dev, "Cannot found pcu device node\n");
> + return;
> + }
> + reg = of_iomap(np, 0) + ZX2967_RESET_MASK_REG;
> + of_node_put(np);
We should use syscon interface to access sysctrl block.
> +
> + val = readl(reg);
> + val &= ~mask;
> + val |= config;
> + writel(val, reg);
> +}
> +
> +static int zx2967_wdt_probe(struct platform_device *pdev)
> +{
> + struct device *dev;
> + struct zx2967_wdt *wdt;
> + struct resource *base;
> + int err, ret = 0;
> + unsigned int rate, val;
> +
> + struct reset_control *rstc;
> +
> + dev = &pdev->dev;
> +
> + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
> + if (!wdt) {
> + ret = -ENOMEM;
> + goto out;
> + }
return -ENOMEM;
> +
> + platform_set_drvdata(pdev, wdt);
> +
> + wdt->dev = dev;
> + spin_lock_init(&wdt->lock);
> +
> + wdt->wdt_device.info = &zx2967_wdt_ident;
> + wdt->wdt_device.ops = &zx2967_wdt_ops;
> + wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT;
> + wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT;
> + wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT;
> + wdt->wdt_device.parent = &pdev->dev;
> +
> + base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + wdt->reg_base = devm_ioremap_resource(dev, base);
It should be error checked.
> +
> + if (of_find_property(dev->of_node, "reset-mask-config", NULL))
> + zx2967_reset_mask_config(dev);
> +
> + wdt->reboot_handler.notifier_call = zx2967_wdt_notify_sys;
> + ret = register_reboot_notifier(&wdt->reboot_handler);
> + wdt->clock = devm_clk_get(dev, "wdtclk");
> + if (IS_ERR(wdt->clock)) {
> + dev_err(dev, "failed to find watchdog clock source\n");
> + ret = PTR_ERR(wdt->clock);
> + goto out;
> + }
> + ret = clk_prepare_enable(wdt->clock);
> + if (ret < 0) {
> + dev_err(dev, "failed to enable clock\n");
> + goto out;
> + }
> +
> + rate = clk_get_rate(wdt->clock);
> + if (rate == 24000000)
> + ret = clk_set_rate(wdt->clock, 32768);
> + rate = clk_get_rate(wdt->clock);
> +
> + rstc = devm_reset_control_get(dev, "wdtrst");
> + if (!rstc) {
> + dev_info(dev, "rstc get failed");
> + } else {
> + reset_control_assert(rstc);
> + mdelay(10);
> + reset_control_deassert(rstc);
> + }
> +
> + watchdog_set_drvdata(&wdt->wdt_device, wdt);
> +
> + watchdog_init_timeout(&wdt->wdt_device, timeout, &pdev->dev);
> + watchdog_set_nowayout(&wdt->wdt_device, nowayout);
> +
> + zx2967_wdt_stop(&wdt->wdt_device);
> +
> + err = watchdog_register_device(&wdt->wdt_device);
> + if (unlikely(err)) {
> + ret = err;
> + goto fail_register;
> + }
> +
> + wdt->restart_handler.notifier_call = zx2967_wdt_restart;
> + wdt->restart_handler.priority = 128;
> + ret = register_restart_handler(&wdt->restart_handler);
> + if (ret) {
> + pr_err("cannot register restart handler, %d\n", ret);
dev_err()
> + goto fail_restart;
> + }
> +
> + val = zx2967_wdt_read_reg(wdt->reg_base + ZX2967_WDT_START_REG);
> + dev_info(&pdev->dev, "watchdog enabled (timeout=%d sec, nowayout=%d)",
> + wdt->wdt_device.timeout, nowayout);
> +
> + return 0;
> +
> +fail_restart:
> + watchdog_unregister_device(&wdt->wdt_device);
> +fail_register:
> + clk_disable_unprepare(wdt->clock);
> +out:
> + return ret;
> +}
> +
> +static int zx2967_wdt_remove(struct platform_device *pdev)
> +{
> + struct zx2967_wdt *wdt = platform_get_drvdata(pdev);
> +
> + unregister_restart_handler(&wdt->restart_handler);
> + watchdog_unregister_device(&wdt->wdt_device);
> + clk_disable_unprepare(wdt->clock);
> +
> + return 0;
> +}
> +
> +static void zx2967_wdt_shutdown(struct platform_device *pdev)
> +{
> + struct zx2967_wdt *wdt = platform_get_drvdata(pdev);
> +
> + if (!(wdt->flags & ZX2967_WDT_FLAG_REBOOT_MON))
> + zx2967_wdt_stop(&wdt->wdt_device);
> +}
> +
> +static const struct of_device_id zx2967_wdt_match[] = {
> + { .compatible = "zte,zx296718-wdt", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, zx2967_wdt_match);
> +
> +static const struct platform_device_id zx2967_wdt_ids[] = {
> + { .name = "zx2967-wdt", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(platform, zx2967_wdt_ids);
This is not needed, as we do not support platform probe but only DT on
ZTE platforms.
Shawn
> +
> +static struct platform_driver zx2967_wdt_driver = {
> + .probe = zx2967_wdt_probe,
> + .remove = zx2967_wdt_remove,
> + .shutdown = zx2967_wdt_shutdown,
> + .id_table = zx2967_wdt_ids,
> + .driver = {
> + .name = "zx2967-wdt",
> + .of_match_table = of_match_ptr(zx2967_wdt_match),
> + },
> +};
> +module_platform_driver(zx2967_wdt_driver);
> +
> +MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
> +MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH v5 4/5] dt: bindings: i2c-mux-pca954x: Add documentation for nxp,irq-mask-enable
From: Peter Rosin @ 2017-01-17 8:57 UTC (permalink / raw)
To: Phil Reid, wsa-z923LK4zBo2bacvFa/9K2g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484640029-22870-5-git-send-email-preid-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>
On 2017-01-17 09:00, Phil Reid wrote:
> Unfortunately some hardware device will assert their irq line immediately
> on power on and provide no mechanism to mask the irq. As the i2c muxes
> provide no method to mask irq line this provides a work around by keeping
> the parent irq masked until enough device drivers have loaded to service
> all pending interrupts.
>
> For example the the ltc1760 assert its SMBALERT irq immediately on power
> on. With two ltc1760 attached to bus 0 & 1 on a pca954x mux when the first
> device is registered irq are enabled and fire continuously as the second
> device driver has not yet loaded. Setting this parameter to <1 1> will
> delay the irq being enabled until both devices are ready.
Hang on, does this suggestion I made make any sense at all? Maybe it does,
but does the pca954x driver even get notified of any but the first irq client
that unmasks interrupts on a mux segment? How can it count the number of
active irq clients if not?
I'm truly sorry for the trouble I'm causing by not just saying how it should
be done from the start, but I feel like I've been thrown in at the deep end
when it comes to interrupt controllers...
Cheers,
peda
> Signed-off-by: Phil Reid <preid-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>
> ---
> Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
> index aa09704..ac71be6 100644
> --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
> +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
> @@ -19,6 +19,8 @@ Optional Properties:
> - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
> children in idle state. This is necessary for example, if there are several
> multiplexers on the bus and the devices behind them use same I2C addresses.
> + - nxp,irq-mask-enable: array; Defines the minimum number of chips that must
> + register an irq for each channel before the parent irq line in enabled.
> - interrupt-parent: Phandle for the interrupt controller that services
> interrupts for this device.
> - interrupts: Interrupt mapping for IRQ.
> @@ -36,6 +38,7 @@ Example:
> #size-cells = <0>;
> reg = <0x74>;
>
> + nxp,irq-mask-enable = <0 0 0 0 1 0 0 0>;
> interrupt-parent = <&ipic>;
> interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
> interrupt-controller;
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] ARM: dts: imx6q-utilite-pro: enable 2nd display pipeline
From: Philipp Zabel @ 2017-01-17 8:57 UTC (permalink / raw)
To: christopher.spinrath
Cc: mark.rutland, devicetree, linux, robh+dt, grinberg, kernel,
fabio.estevam, shawnguo, linux-arm-kernel
In-Reply-To: <ec9676f344eb4786a28a3c7b969f0e94@rwthex-s1-b.rwth-ad.de>
Hi Christopher,
On Fri, 2016-12-02 at 15:37 +0100, christopher.spinrath@rwth-aachen.de
wrote:
> From: Christopher Spinrath <christopher.spinrath@rwth-aachen.de>
>
> Apart from the already enabled Designware HDMI port, the Utilite Pro
> has a second display pipeline which has the following shape:
>
> IPU1 DI0 --> Parallel display --> tfp410 rgb24 to DVI encoder
> --> HDMI connector.
> Enable support for it.
>
> In addition, since this pipeline is hardwired to IPU1, sever the link
> between IPU1 and the SoC-internal Designware HDMI encoder forcing the
> latter to be connected to IPU2 instead of IPU1. Otherwise, it is not
> possible to drive both displays at high resolution due to the bandwidth
> limitations of a single IPU.
>
> Signed-off-by: Christopher Spinrath <christopher.spinrath@rwth-aachen.de>
>
> ---
>
> Hi all,
>
> the removal of the link between IPU1 and the Designware HDMI encoder is the
> result of a discussion I had with Philipp Zabel:
>
> https://lists.freedesktop.org/archives/dri-devel/2016-November/125399.html .
>
> Altough it is not possible to connect anything else to IPU1 on the Utilite, this
> approach has at least one disadvantage: if the resolution is low enough such
> that a single IPU can handle both displays then muxing both displays to IPU1
> would reduce the power consumption.
>
> However, IMHO omitting the link IPU1 <--> DW HDMI is still the preferrable
> solution since I'm not aware of any OS/driver that is capable of switching IPUs
> or can handle the bandwidth limitation in a sane way. In particular, Linux is
> unusable when both displays are supposed to be driven at high resolution and
> both muxing options for the DW HDMI are available (this is not a userspace
> issue; the system becomes almost unresponsive as soon as the kernel sets the
> initial resolution).
>
> Cheers,
> Christopher
>
> P.S.: this patch depends on the tfp410 bridge driver which has recently been
> merged into drm-next.
>
> arch/arm/boot/dts/imx6q-utilite-pro.dts | 115 ++++++++++++++++++++++++++++++++
> 1 file changed, 115 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx6q-utilite-pro.dts b/arch/arm/boot/dts/imx6q-utilite-pro.dts
> index 2200994..69bdd82 100644
> --- a/arch/arm/boot/dts/imx6q-utilite-pro.dts
> +++ b/arch/arm/boot/dts/imx6q-utilite-pro.dts
> @@ -59,6 +59,33 @@
> rtc1 = &snvs_rtc;
> };
>
> + encoder {
> + compatible = "ti,tfp410";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> +
> + tfp410_in: endpoint {
> + remote-endpoint = <¶llel_display_out>;
> + };
> + };
> +
> + port@1 {
> + reg = <1>;
> +
> + tfp410_out: endpoint {
> + remote-endpoint = <&hdmi_connector_in>;
> + };
> + };
> + };
> + };
> +
> gpio-keys {
> compatible = "gpio-keys";
> pinctrl-names = "default";
> @@ -72,6 +99,19 @@
> };
> };
>
> + hdmi-connector {
> + compatible = "hdmi-connector";
> +
> + type = "a";
> + ddc-i2c-bus = <&i2c_dvi_ddc>;
> +
> + port {
> + hdmi_connector_in: endpoint {
> + remote-endpoint = <&tfp410_out>;
> + };
> + };
> + };
> +
> i2cmux {
> compatible = "i2c-mux-gpio";
> pinctrl-names = "default";
> @@ -105,8 +145,46 @@
> #size-cells = <0>;
> };
> };
> +
> + parallel-display {
> + compatible = "fsl,imx-parallel-display";
> + #address-cells = <1>;
> + #size-cells = <0>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_ipu1>;
> +
> + interface-pix-fmt = "rgb24";
This is not necessary if the connector created by the tpf410 has the
correct media bus format set in its display_info structure. This can be
done in tfp410_attach, before calling drm_mode_connector_attach_encoder:
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
drm_display_info_set_bus_formats(&dvi->connector.display_info,
&bus_format, 1);
After this is done, the above line should be removed in a follow-up
patch.
> + port@0 {
> + reg = <0>;
> +
> + parallel_display_in: endpoint {
> + remote-endpoint = <&ipu1_di0_disp0>;
> + };
> + };
> +
> + port@1 {
> + reg = <1>;
> +
> + parallel_display_out: endpoint {
> + remote-endpoint = <&tfp410_in>;
> + };
> + };
> + };
> };
>
> +/*
> + * A single IPU is not able to drive both display interfaces available on the
> + * Utilite Pro at high resolution due to its bandwidth limitation. Since the
> + * tfp410 encoder is wired up to IPU1, sever the link between IPU1 and the
> + * SoC-internal Designware HDMI encoder forcing the latter to be connected to
> + * IPU2 instead of IPU1.
> + */
> +/delete-node/&ipu1_di0_hdmi;
> +/delete-node/&hdmi_mux_0;
> +/delete-node/&ipu1_di1_hdmi;
> +/delete-node/&hdmi_mux_1;
> +
> &hdmi {
> ddc-i2c-bus = <&i2c2>;
> status = "okay";
> @@ -151,6 +229,39 @@
> >;
> };
>
> + pinctrl_ipu1: ipu1grp {
> + fsl,pins = <
> + MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x38
> + MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x38
> + MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x38
> + MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x38
> + MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x38
> + MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x38
> + MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x38
> + MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x38
> + MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x38
> + MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x38
> + MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x38
> + MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x38
> + MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x38
> + MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x38
> + MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x38
> + MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x38
> + MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x38
> + MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x38
> + MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x38
> + MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x38
> + MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x38
> + MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x38
> + MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x38
> + MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x38
> + MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x38
> + MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x38
> + MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x38
> + MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x38
> + >;
> + };
> +
> pinctrl_uart2: uart2grp {
> fsl,pins = <
> MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1
> @@ -194,6 +305,10 @@
> };
> };
>
> +&ipu1_di0_disp0 {
> + remote-endpoint = <¶llel_display_in>;
> +};
> +
> &pcie {
> pcie@0,0 {
> reg = <0x000000 0 0 0 0>;
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
regards
Philipp
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox