7019 lines
216 KiB
Diff
7019 lines
216 KiB
Diff
From: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
|
|
Subject: OKI Semiconductor PCH USB Gadget driver
|
|
|
|
This driver implements PCH Gadget controls for PCH.
|
|
|
|
Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
|
|
Acked-by: Wang Qi <qi.wang@intel.com>
|
|
|
|
---
|
|
drivers/usb/gadget/gadget_chips.h | 8 ++
|
|
drivers/usb/gadget/Kconfig | 16
|
|
drivers/usb/gadget/Makefile | 6
|
|
drivers/usb/gadget/pch_common.h | 146
|
|
drivers/usb/gadget/pch_debug.h | 160
|
|
drivers/usb/gadget/pch_udc.c | 2530
|
|
drivers/usb/gadget/pch_udc.h | 172
|
|
drivers/usb/gadget/pch_udc_hal.c | 1110
|
|
drivers/usb/gadget/pch_udc_hal.h | 1829
|
|
drivers/usb/gadget/pch_udc_intr.c | 396
|
|
drivers/usb/gadget/pch_udc_pci.c | 549
|
|
drivers/usb/gadget/pch_udc_pci.h | 97
|
|
+++++++++++++++++++++++++++++++ 12 files changed, zz insertions(+)
|
|
---
|
|
drivers/usb/gadget/Kconfig | 16
|
|
drivers/usb/gadget/Makefile | 6
|
|
drivers/usb/gadget/gadget_chips.h | 12
|
|
drivers/usb/gadget/pch_common.h | 146 ++
|
|
drivers/usb/gadget/pch_debug.h | 60
|
|
drivers/usb/gadget/pch_udc.c | 2530 ++++++++++++++++++++++++++++++++++++++
|
|
drivers/usb/gadget/pch_udc.h | 172 ++
|
|
drivers/usb/gadget/pch_udc_hal.c | 1110 ++++++++++++++++
|
|
drivers/usb/gadget/pch_udc_hal.h | 1829 +++++++++++++++++++++++++++
|
|
drivers/usb/gadget/pch_udc_intr.c | 396 +++++
|
|
drivers/usb/gadget/pch_udc_pci.c | 549 ++++++++
|
|
drivers/usb/gadget/pch_udc_pci.h | 97 +
|
|
12 files changed, 6923 insertions(+)
|
|
|
|
--- a/drivers/usb/gadget/Kconfig
|
|
+++ b/drivers/usb/gadget/Kconfig
|
|
@@ -220,6 +220,22 @@ config USB_OTG
|
|
|
|
Select this only if your OMAP board has a Mini-AB connector.
|
|
|
|
+config USB_GADGET_PCH
|
|
+ boolean "PCH USB Dev"
|
|
+ depends on PCI
|
|
+ select USB_GADGET_DUALSPEED
|
|
+ help
|
|
+ PCH USB device is a PCI based USB peripheral controller which
|
|
+ supports both full and high speed USB 2.0 data transfers.
|
|
+
|
|
+config PCH_USBDEV
|
|
+ tristate
|
|
+ depends on USB_GADGET_PCH
|
|
+ default USB_GADGET
|
|
+ select USB_GADGET_SELECTED
|
|
+
|
|
+
|
|
+
|
|
config USB_GADGET_PXA25X
|
|
boolean "PXA 25x or IXP 4xx"
|
|
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
|
|
--- a/drivers/usb/gadget/Makefile
|
|
+++ b/drivers/usb/gadget/Makefile
|
|
@@ -58,3 +58,9 @@ obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc
|
|
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
|
|
obj-$(CONFIG_USB_STILL_IMAGE) += g_still_image.o
|
|
|
|
+ifeq ($(CONFIG_USB_GADGET_DEBUG),y)
|
|
+ EXTRA_CFLAGS += -DDMA_PPB_MODE
|
|
+endif
|
|
+obj-$(CONFIG_PCH_USBDEV) += pch_usbdev.o
|
|
+pch_usbdev-objs := pch_udc_pci.o pch_udc.o pch_udc_hal.o pch_udc_intr.o
|
|
+
|
|
--- a/drivers/usb/gadget/gadget_chips.h
|
|
+++ b/drivers/usb/gadget/gadget_chips.h
|
|
@@ -180,6 +180,14 @@
|
|
#endif
|
|
|
|
|
|
+#ifdef CONFIG_USB_GADGET_PCH
|
|
+#define gadget_is_ioh(g) (!strcmp("ioh_udc", (g)->name))
|
|
+#else
|
|
+#define gadget_is_ioh(g) 0
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
/**
|
|
* usb_gadget_controller_number - support bcdDevice id convention
|
|
* @gadget: the controller being driven
|
|
@@ -247,6 +255,10 @@ static inline int usb_gadget_controller_
|
|
return 0x24;
|
|
else if (gadget_is_r8a66597(gadget))
|
|
return 0x25;
|
|
+
|
|
+ else if (gadget_is_ioh(gadget))
|
|
+ return 0x26;
|
|
+
|
|
return -ENOENT;
|
|
}
|
|
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_common.h
|
|
@@ -0,0 +1,146 @@
|
|
+/*!
|
|
+ * @file ioh_common.h
|
|
+ * @brief Provides the macro definitions used by all files.
|
|
+ * @version 1.0.0.0
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * History:
|
|
+ * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * WIPRO 03/07/2009
|
|
+ * modified:
|
|
+ * WIPRO 05/08/2009
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __IOH_COMMON_H__
|
|
+#define __IOH_COMMON_H__
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_WRITE8
|
|
+@brief Macro for writing 8 bit data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE8(val, addr) iowrite8((val), (void __iomem *)(addr))
|
|
+/*! @ingroup Global
|
|
+@def IOH_LOG
|
|
+@brief Macro for writing 16 bit data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE16(val, addr) iowrite16((val), (void __iomem *)(addr))
|
|
+/*! @ingroup Global
|
|
+@def IOH_LOG
|
|
+@brief Macro for writing 32 bit data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE32(val, addr) iowrite32((val), (void __iomem *)(addr))
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ8
|
|
+@brief Macro for reading 8 bit data from an io/mem address
|
|
+*/
|
|
+#define IOH_READ8(addr) ioread8((void __iomem *)(addr))
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ16
|
|
+@brief Macro for reading 16 bit data from an io/mem address
|
|
+*/
|
|
+#define IOH_READ16(addr) ioread16((void __iomem *)(addr))
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ32
|
|
+@brief Macro for reading 32 bit data from an io/mem address
|
|
+*/
|
|
+#define IOH_READ32(addr) ioread32((void __iomem *)(addr))
|
|
+/*! @ingroup Global
|
|
+@def IOH_WRITE32_F
|
|
+@brief Macro for writing 32 bit data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE32_F(val, addr) do \
|
|
+ { IOH_WRITE32((val), (addr)); (void)IOH_READ32((addr)); } while (0);
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_WRITE_BYTE
|
|
+@brief Macro for writing 1 byte data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE_BYTE IOH_WRITE8
|
|
+/*! @ingroup Global
|
|
+@def IOH_WRITE_WORD
|
|
+@brief Macro for writing 1 word data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE_WORD IOH_WRITE16
|
|
+/*! @ingroup Global
|
|
+@def IOH_WRITE_LONG
|
|
+@brief Macro for writing long data to an io/mem address
|
|
+*/
|
|
+#define IOH_WRITE_LONG IOH_WRITE32
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_BYTE
|
|
+@brief Macro for reading 1 byte data from an io/mem address
|
|
+*/
|
|
+#define IOH_READ_BYTE IOH_READ8
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_WORD
|
|
+@brief Macro for reading 1 word data from an io/mem address
|
|
+*/
|
|
+#define IOH_READ_WORD IOH_READ16
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief Macro for reading long data from an io/mem address
|
|
+*/
|
|
+#define IOH_READ_LONG IOH_READ32
|
|
+
|
|
+/* Bit Manipulation Macros */
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief macro to set a specified bit(mask) at the
|
|
+ specified address
|
|
+*/
|
|
+#define IOH_SET_ADDR_BIT(addr, bitmask) IOH_WRITE_LONG((IOH_READ_LONG(addr) |\
|
|
+ (bitmask)), (addr))
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief macro to clear a specified bit(mask) at the specified address
|
|
+*/
|
|
+#define IOH_CLR_ADDR_BIT(addr, bitmask) IOH_WRITE_LONG((IOH_READ_LONG(addr) &\
|
|
+ ~(bitmask)), (addr))
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief macro to set a specified bitmask for a variable
|
|
+*/
|
|
+#define IOH_SET_BITMSK(var, bitmask) ((var) |= (bitmask))
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief macro to clear a specified bitmask for a variable
|
|
+*/
|
|
+#define IOH_CLR_BITMSK(var, bitmask) ((var) &= (~(bitmask)))
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief macro to set a specified bit for a variable
|
|
+*/
|
|
+#define IOH_SET_BIT(var, bit) ((var) |= (1<<(bit)))
|
|
+
|
|
+/*! @ingroup Global
|
|
+@def IOH_READ_LONG
|
|
+@brief macro to clear a specified bit for a variable
|
|
+*/
|
|
+#define IOH_CLR_BIT(var, bit) ((var) &= ~(1<<(bit)))
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_debug.h
|
|
@@ -0,0 +1,60 @@
|
|
+/*!
|
|
+ * @file ioh_debug.h
|
|
+ * @brief Provides the macro definitions used for debugging.
|
|
+ * @version 1.0.0.0
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * History:
|
|
+ * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * WIPRO 03/07/2009
|
|
+ * modified:
|
|
+ * WIPRO 05/08/2009
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __IOH_DEBUG_H__
|
|
+#define __IOH_DEBUG_H__
|
|
+
|
|
+#ifdef MODULE
|
|
+#define IOH_LOG(level, fmt, args...) printk(level "%s:" fmt "\n",\
|
|
+ THIS_MODULE->name, ##args)
|
|
+#else
|
|
+#define IOH_LOG(level, fmt, args...) printk(level "%s:" fmt "\n" ,\
|
|
+ __FILE__, ##args)
|
|
+#endif
|
|
+
|
|
+
|
|
+#ifdef DEBUG
|
|
+ #define IOH_DEBUG(fmt, args...) IOH_LOG(KERN_DEBUG, fmt, ##args)
|
|
+#else
|
|
+ #define IOH_DEBUG(fmt, args...)
|
|
+#endif
|
|
+
|
|
+#ifdef IOH_TRACE_ENABLED
|
|
+ #define IOH_TRACE IOH_DEBUG
|
|
+#else
|
|
+ #define IOH_TRACE(fmt, args...)
|
|
+#endif
|
|
+
|
|
+#define IOH_TRACE_ENTER IOH_TRACE("Enter %s", __func__)
|
|
+#define IOH_TRACE_EXIT IOH_TRACE("Exit %s", __func__)
|
|
+
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc.c
|
|
@@ -0,0 +1,2530 @@
|
|
+ /*!
|
|
+ *@file ioh_udc.c
|
|
+ *@brief This file contains the definitions for IOH UDC driver APIs.
|
|
+ *
|
|
+ *The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ *It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or interrupt
|
|
+ * type).
|
|
+ *
|
|
+ *The IOH USB device controller driver provides required interface
|
|
+ *to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ *@version 0.96
|
|
+ *
|
|
+ *@section
|
|
+ *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 of the License.
|
|
+ *
|
|
+ *This program is distributed in the hope that it will be useful,
|
|
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ *GNU General Public License for more details.
|
|
+ *
|
|
+ *You should have received a copy of the GNU General Public License
|
|
+ *along with this program; if not, write to the Free Software
|
|
+ *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ *History:
|
|
+ *Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ *All rights reserved.
|
|
+ *
|
|
+ *created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ *modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/smp_lock.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/usb/ch9.h>
|
|
+#include <linux/usb/gadget.h>
|
|
+
|
|
+#include "pch_common.h"
|
|
+#include "pch_debug.h"
|
|
+
|
|
+#include "pch_udc_hal.h"
|
|
+#include "pch_udc.h"
|
|
+
|
|
+#if 0 /* statis as static */
|
|
+#define static
|
|
+#endif
|
|
+
|
|
+/* stall spin lock */
|
|
+static DEFINE_SPINLOCK(udc_stall_spinlock);
|
|
+
|
|
+/* function prototypes */
|
|
+static int ioh_udc_pcd_get_frame(struct usb_gadget *gadget);
|
|
+static int ioh_udc_pcd_wakeup(struct usb_gadget *gadget);
|
|
+static int ioh_udc_pcd_selfpowered(struct usb_gadget *gadget, int value);
|
|
+static int ioh_udc_pcd_pullup(struct usb_gadget *gadget, int is_on);
|
|
+static int ioh_udc_pcd_vbus_session(struct usb_gadget *gadget, int is_active);
|
|
+static int ioh_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA);
|
|
+static void ioh_udc_pcd_reinit(struct ioh_udc_dev *dev);
|
|
+static void complete_req(struct ioh_udc_ep *ep, struct ioh_udc_request *req,
|
|
+ int status);
|
|
+static int ioh_udc_pcd_ep_enable(struct usb_ep *usbep,
|
|
+ const struct usb_endpoint_descriptor *desc);
|
|
+static int ioh_udc_pcd_ep_disable(struct usb_ep *usbep);
|
|
+static struct usb_request *ioh_udc_alloc_request(struct usb_ep *uep, gfp_t gfp);
|
|
+static int ioh_udc_free_dma_chain(struct ioh_udc_dev *dev,
|
|
+ struct ioh_udc_request *req);
|
|
+static int ioh_udc_create_dma_chain(struct ioh_udc_ep *ep,
|
|
+ struct ioh_udc_request *req,
|
|
+ unsigned long buf_len, gfp_t gfp_flags);
|
|
+static void ioh_udc_free_request(struct usb_ep *uep, struct usb_request *req);
|
|
+static int prepare_dma(struct ioh_udc_ep *ep,
|
|
+ struct ioh_udc_request *req, gfp_t gfp);
|
|
+static void process_zlp(struct ioh_udc_ep *ep, struct ioh_udc_request *req);
|
|
+static int ioh_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
|
|
+ gfp_t gfp);
|
|
+static int ioh_udc_pcd_dequeue(struct usb_ep *usbep,
|
|
+ struct usb_request *usbreq);
|
|
+static int ioh_udc_pcd_set_halt(struct usb_ep *usbep, int halt);
|
|
+static int ioh_udc_pcd_set_wedge(struct usb_ep *usbep);
|
|
+static void ioh_udc_pcd_fifo_flush(struct usb_ep *usbep);
|
|
+static void ioh_udc_svc_data_out(struct ioh_udc_dev *dev, int ep_num);
|
|
+static void ioh_udc_svc_control_in(struct ioh_udc_dev *dev);
|
|
+static void ioh_udc_svc_control_out(struct ioh_udc_dev *dev);
|
|
+static void ioh_udc_svc_data_in(struct ioh_udc_dev *dev, int ep_num);
|
|
+static void ioh_udc_read_all_epstatus(struct ioh_udc_dev *dev, u32 ep_intr);
|
|
+static void ioh_udc_setup_ep0(struct ioh_udc_dev *dev);
|
|
+static void ioh_udc_init_setup_buff(struct ioh_udc_stp_dma_desc *td_stp);
|
|
+static void ioh_udc_complete_transfer(struct ioh_udc_ep *ep);
|
|
+static void ioh_udc_complete_receiver(struct ioh_udc_ep *ep);
|
|
+static void ioh_udc_start_next_txrequest(struct ioh_udc_ep *ep);
|
|
+static void ioh_udc_start_rxrequest(struct ioh_udc_ep *ep,
|
|
+ struct ioh_udc_request *req);
|
|
+static void ioh_udc_postsvc_epinters(struct ioh_udc_dev *dev, int ep_num);
|
|
+
|
|
+/* gadget operations */
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ *@struct ioh_udc_ops
|
|
+ *@brief specifies gadget operations possible
|
|
+ */
|
|
+const struct usb_gadget_ops ioh_udc_ops = {
|
|
+ .get_frame = ioh_udc_pcd_get_frame,
|
|
+ .wakeup = ioh_udc_pcd_wakeup,
|
|
+ .set_selfpowered = ioh_udc_pcd_selfpowered,
|
|
+ .pullup = ioh_udc_pcd_pullup,
|
|
+ .vbus_session = ioh_udc_pcd_vbus_session,
|
|
+ .vbus_draw = ioh_udc_pcd_vbus_draw,
|
|
+};
|
|
+
|
|
+
|
|
+/* endpoint interface */
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ *@struct usb_ep_ops ioh_udc_ep_ops
|
|
+ *@brief specifies endpoint operations possible
|
|
+ */
|
|
+static const struct usb_ep_ops ioh_udc_ep_ops = {
|
|
+ .enable = ioh_udc_pcd_ep_enable,
|
|
+ .disable = ioh_udc_pcd_ep_disable,
|
|
+ .alloc_request = ioh_udc_alloc_request,
|
|
+ .free_request = ioh_udc_free_request,
|
|
+ .queue = ioh_udc_pcd_queue,
|
|
+ .dequeue = ioh_udc_pcd_dequeue,
|
|
+ .set_halt = ioh_udc_pcd_set_halt,
|
|
+ .set_wedge = ioh_udc_pcd_set_wedge,
|
|
+ .fifo_status = NULL,
|
|
+ .fifo_flush = ioh_udc_pcd_fifo_flush,
|
|
+};
|
|
+
|
|
+/* received setup data */
|
|
+static union ioh_udc_setup_data setup_data;
|
|
+static unsigned long ep0out_buf[64];
|
|
+static dma_addr_t dma_addr;
|
|
+
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_get_frame(struct usb_gadget *gadget)
|
|
+ *@brief This API is invoked to get the current frame number
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the argument is NULL, return -EINVAL
|
|
+ * - Return the frame number by invoking HAL API
|
|
+ * ioh_udc_get_frame
|
|
+ *@param gadget Reference to the gadget driver
|
|
+ *@return int [ the frame number ] -EINVAL [Invalid Arguments]
|
|
+ *@see
|
|
+ * - ioh_udc_get_frame
|
|
+ */
|
|
+static int ioh_udc_pcd_get_frame(struct usb_gadget *gadget)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_get_frame: enter");
|
|
+ if (gadget == NULL) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_get_frame: exit -EINVAL");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev = container_of(gadget, struct ioh_udc_dev, gadget);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_get_frame: exit");
|
|
+ return ioh_udc_get_frame(dev->regs);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_wakeup(struct usb_gadget *gadget)
|
|
+ *@brief This API is invoked to initiate a remote wakeup
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the argument is NULL, return -EINVAL
|
|
+ * - Invoke HAL API ioh_udc_rmt_wakeup to start
|
|
+ * remote signaling
|
|
+ *@param gadget Reference to the gadget driver
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_rmt_wakeup
|
|
+ */
|
|
+static int ioh_udc_pcd_wakeup(struct usb_gadget *gadget)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+ unsigned long flags;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_wakeup: enter");
|
|
+ if (gadget == NULL) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_wakeup: exit -EINVAL");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev = container_of(gadget, struct ioh_udc_dev, gadget);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_wakeup: initiate remote wakeup");
|
|
+ spin_lock_irqsave(&dev->lock, flags);
|
|
+ ioh_udc_rmt_wakeup(dev->regs);
|
|
+ spin_unlock_irqrestore(&dev->lock, flags);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_wakeup: exit");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_selfpowered (struct usb_gadget *gadget,
|
|
+ * int value)
|
|
+ *@brief This API is invoked to specify whether the device is self
|
|
+ * powered or not
|
|
+ *@remarks The following actions are performed:
|
|
+ *- if value is 0, invoke HAL API ioh_udc_clear_selfpowered to clear the self
|
|
+ * powered feature
|
|
+ *- otherwise, invoke ioh_udc_set_selfpowered to set the self powered
|
|
+ * feature for the device
|
|
+ *@param gadget Reference to the gadget driver
|
|
+ *@param value specifies self powered or not
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_set_selfpowered
|
|
+ * - ioh_udc_clear_selfpowered
|
|
+ */
|
|
+static int ioh_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_selfpowered: enter");
|
|
+ if (gadget == NULL) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_selfpowered: exit -EINVAL");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev = container_of(gadget, struct ioh_udc_dev, gadget);
|
|
+ if (value == 0)
|
|
+ ioh_udc_clear_selfpowered(dev->regs);
|
|
+ else
|
|
+ ioh_udc_set_selfpowered(dev->regs);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_selfpowered: exit value=%d", value);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_pullup (struct usb_gadget *gadget,
|
|
+ * int is_on)
|
|
+ *@brief This API is invoked to make the device visible/invisible to the host
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If gadget passed is NULL, return -EINVAL
|
|
+ * - If is_on is TRUE, call ioh_udc_clear_disconnect()
|
|
+ * to make the device visible to the host
|
|
+ * - Otherwise call the HAL API ioh_udc_set_disconnect()
|
|
+ * to make the device unavailable to the host
|
|
+ *@param gadget Reference to the gadget driver
|
|
+ *@param is_on specifies whether the pull up is made active
|
|
+ * or inactive
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_clear_disconnect
|
|
+ * - ioh_udc_set_disconnect
|
|
+ */
|
|
+static int ioh_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_pullup: enter");
|
|
+ if (gadget == NULL) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_pullup: exit -EINVAL");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev = container_of(gadget, struct ioh_udc_dev, gadget);
|
|
+ if (is_on == 0)
|
|
+ ioh_udc_set_disconnect(dev->regs);
|
|
+ else
|
|
+ ioh_udc_clear_disconnect(dev->regs);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_pullup: exit is_on=%d", is_on);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_vbus_session (struct usb_gadget *gadget,
|
|
+ * int is_active)
|
|
+ *@brief This API is used by a driver for an external transceiver
|
|
+ * (or GPIO) that
|
|
+ * detects a VBUS power session starting/ending
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the gadget passed is NULL, return -EINVAL
|
|
+ * - Invoke the HAL API ioh_udc_vbus_session to notify
|
|
+ * the start/end of the vbus power
|
|
+ *@param gadget Reference to the gadget driver
|
|
+ *@param is_active specifies whether the session is starting or ending
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_vbus_session
|
|
+ */
|
|
+static int ioh_udc_pcd_vbus_session(struct usb_gadget *gadget, int is_active)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_vbus_session: enter");
|
|
+ if (gadget == NULL) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_vbus_session: exit -EINVAL");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ dev = container_of(gadget, struct ioh_udc_dev, gadget);
|
|
+
|
|
+ ioh_udc_vbus_session(dev->regs, is_active);
|
|
+ IOH_DEBUG("ioh_udc_pcd_vbus_session: exit");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_vbus_draw (struct usb_gadget *gadget,
|
|
+ * unsigned int mA)
|
|
+ *@brief This API is used by gadget drivers during SET_CONFIGURATION
|
|
+ * calls to
|
|
+ * specify how much power the device can consume
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the gadget passed is NULL, return -EINVAL
|
|
+ * - Return -EOPNOTSUPP
|
|
+ *@param gadget Reference to the gadget driver
|
|
+ *@param mA specifies the current limit in 2mA unit
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ */
|
|
+static int ioh_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
|
|
+{
|
|
+ IOH_DEBUG("ioh_udc_pcd_vbus_draw: enter");
|
|
+ if ((gadget == NULL) || (mA > 250)) { /* Max is 250 in 2mA unit */
|
|
+ IOH_DEBUG("ioh_udc_pcd_vbus_draw: exit -EINVAL");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ IOH_DEBUG("ioh_udc_pcd_vbus_draw: exit -EOPNOTSUPP");
|
|
+
|
|
+ /* Could not find any regs where we can set the limit */
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+const char ep0_string[] = "ep0in";
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void ioh_udc_pcd_reinit(struct ioh_udc_dev *dev)
|
|
+ *@brief This API initializes the endpoint structures
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Initialize gadgets speed as unknown
|
|
+ *- Initialize the gadget endpoint list (ep_list)
|
|
+ *- Assign name for each endpoint and their reference to endpoint operations
|
|
+ * to ioh_udc_ep_ops
|
|
+ *- Add all endpoints other than ep0 IN and ep0 OUT to the gadgete ep_list
|
|
+ *@param dev Reference to the driver structure
|
|
+ *@return none
|
|
+ */
|
|
+static void ioh_udc_pcd_reinit(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ static const char *ep_string[] = {
|
|
+ ep0_string, "ep0out",
|
|
+ "ep1in", "ep1out",
|
|
+ "ep2in", "ep2out",
|
|
+ "ep3in", "ep3out",
|
|
+ "ep4in", "ep4out",
|
|
+ "ep5in", "ep5out",
|
|
+ "ep6in", "ep6out",
|
|
+ "ep7in", "ep7out",
|
|
+ "ep8in", "ep8out",
|
|
+ "ep9in", "ep9out",
|
|
+ "ep10in", "ep10out",
|
|
+ "ep11in", "ep11out",
|
|
+ "ep12in", "ep12out",
|
|
+ "ep13in", "ep13out",
|
|
+ "ep14in", "ep14out",
|
|
+ "ep15in", "ep15out",
|
|
+ };
|
|
+ int i;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_reinit: enter");
|
|
+
|
|
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
|
|
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
|
|
+
|
|
+ /* Initialize the endpoints structures */
|
|
+ for (i = 0; i < IOH_UDC_EP_NUM; i++) {
|
|
+ struct ioh_udc_ep *ep = &dev->ep[i];
|
|
+ memset(ep, 0, sizeof(*ep));
|
|
+
|
|
+ ep->desc = NULL;
|
|
+ ep->dev = dev;
|
|
+ ep->halted = 1;
|
|
+ ep->num = i / 2;
|
|
+ ep->in = ((i & 1) == 0) ? 1 : 0;
|
|
+
|
|
+ ep->ep.name = ep_string[i];
|
|
+ ep->ep.ops = &ioh_udc_ep_ops;
|
|
+ if (ep->in)
|
|
+ ep->regs = (struct ioh_udc_ep_regs *)\
|
|
+ ((int)dev->ep_regs + ep->num * UDC_EP_REG_OFS);
|
|
+ else
|
|
+ ep->regs = (struct ioh_udc_ep_regs *)\
|
|
+ ((int)dev->ep_regs + \
|
|
+ (UDC_EPINT_OUT_EP0 + ep->num) * UDC_EP_REG_OFS);
|
|
+
|
|
+ ep->dma = &ep->regs->epctl;
|
|
+ /* need to set ep->ep.maxpacket and set Default Configuration?*/
|
|
+ ep->ep.maxpacket = UDC_BULK_MAX_PKT_SIZE;
|
|
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
|
|
+ INIT_LIST_HEAD(&ep->queue);
|
|
+ }
|
|
+ dev->ep[UDC_EP0IN_IDX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
|
|
+ dev->ep[UDC_EP0OUT_IDX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
|
|
+
|
|
+ dma_addr = pci_map_single(dev->pdev, ep0out_buf,
|
|
+ 256, PCI_DMA_FROMDEVICE);
|
|
+
|
|
+ /* remove ep0 in and out from the list. They have own pointer */
|
|
+ list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list);
|
|
+ list_del_init(&dev->ep[UDC_EP0OUT_IDX].ep.ep_list);
|
|
+
|
|
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
|
|
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
|
|
+ IOH_DEBUG("ioh_udc_pcd_reinit: exit");
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn int ioh_udc_pcd_init(struct ioh_udc_dev *dev)
|
|
+ *@brief This API initializes the driver structure
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Set udc csr register base in dev->csr
|
|
+ *- Set Device Configuration Register base in dev->regs
|
|
+ *- Set Endpoint-specific CSR base in dev->ep_regs
|
|
+ *- Invoke ioh_udc_init to initialize registers, interrupts
|
|
+ *- Invoke ioh_udc_pcd_reinit to perform initialization of the endpoint
|
|
+ * structures
|
|
+ *- Return Success (0)
|
|
+ *@param dev Reference to the driver structure
|
|
+ *@return success
|
|
+ *@see
|
|
+ * - ioh_udc_init
|
|
+ * - ioh_udc_pcd_reinit
|
|
+ */
|
|
+int ioh_udc_pcd_init(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ IOH_DEBUG("ioh_udc_pcd_init: enter");
|
|
+
|
|
+ /* udc csr registers base */
|
|
+ dev->csr = dev->virt_addr + UDC_CSR_ADDR;
|
|
+ /* dev registers base */
|
|
+ dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
|
|
+ /* ep registers base */
|
|
+ dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
|
|
+
|
|
+ /* init registers, interrupts, ... */
|
|
+ ioh_udc_init(dev->regs);
|
|
+#ifdef IOH_PRINT_REG
|
|
+ ioh_udc_print_regs((u32)dev->virt_addr);
|
|
+#endif
|
|
+ ioh_udc_pcd_reinit(dev);
|
|
+ IOH_DEBUG("ioh_udc_pcd_init: exit");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
|
+ *@brief This API is used to make this driver available to the Linux USB
|
|
+ * gadget framework
|
|
+ *@remarks The following actions are performed:
|
|
+ *- If the argument is NULL or if it doesnt have necessary callbacks,
|
|
+ * return -EINVAL
|
|
+ *- If any class driver is already bound, return -EBUSY
|
|
+ *- If drivers structure is not allocated, return -ENODEV
|
|
+ *- Hook up the gadget driver by invoking the drivers bind routine
|
|
+ *- Invoke ioh_udc_setup_ep0 to get ready for endpoint 0 traffic
|
|
+ *- Enable the host detection and resume signaling on USB
|
|
+ *@param driver Reference to the USB gadget driver structure
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_setup_ep0
|
|
+ * - ioh_udc_clear_disconnect
|
|
+ */
|
|
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = ioh_udc;
|
|
+ int retval;
|
|
+
|
|
+ IOH_DEBUG("usb_gadget_register_driver: enter");
|
|
+ if ((driver == NULL) || (driver->speed == USB_SPEED_UNKNOWN) ||
|
|
+ (driver->bind == NULL) || (driver->setup == NULL) ||
|
|
+ (driver->unbind == NULL) || (driver->disconnect == NULL)) {
|
|
+ IOH_LOG(KERN_ERR, "usb_gadget_register_driver: invalid\
|
|
+ driver parameter");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (dev == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (dev->driver != NULL) {
|
|
+ IOH_LOG(KERN_ERR, "usb_gadget_register_driver: already bound");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ driver->driver.bus = NULL;
|
|
+ dev->driver = driver;
|
|
+ dev->gadget.dev.driver = &driver->driver;
|
|
+
|
|
+ /* Invoke the bind routine of the gadget driver */
|
|
+ retval = driver->bind(&dev->gadget);
|
|
+
|
|
+ if (retval != 0) {
|
|
+ IOH_LOG(KERN_ERR, "usb_gadget_register_driver: binding to\
|
|
+ %s returning %d",
|
|
+ driver->driver.name, retval);
|
|
+ dev->driver = NULL;
|
|
+ dev->gadget.dev.driver = NULL;
|
|
+ return retval;
|
|
+ }
|
|
+ /* get ready for ep0 traffic */
|
|
+ ioh_udc_setup_ep0(dev);
|
|
+
|
|
+ /* clear SD */
|
|
+ ioh_udc_clear_disconnect(dev->regs);
|
|
+
|
|
+ dev->connected = 1;
|
|
+
|
|
+ IOH_DEBUG("usb_gadget_register_driver: exit");
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(usb_gadget_register_driver);
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
|
+ *@brief This API is used to make this driver unavailable to the Linux
|
|
+ * USB gadget framework
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the argument is NULL or if it doesnt match
|
|
+ * with the driver already bound, return -EINVAL
|
|
+ * - Call the function drivers unbind method
|
|
+ * - Disable the host detection and stop signaling
|
|
+ *@param driver Reference to the USB gadget driver structure
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_set_disconnect
|
|
+ */
|
|
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = ioh_udc;
|
|
+
|
|
+ IOH_DEBUG("usb_gadget_unregister_driver: enter");
|
|
+ if (dev == NULL)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if ((driver == NULL) || (driver != dev->driver)) {
|
|
+ IOH_LOG(KERN_ERR, "usb_gadget_unregister_driver: invalid\
|
|
+ driver parameter");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ioh_udc_disable_interrupts(dev->regs, UDC_DEVINT_MSK);
|
|
+
|
|
+ /* Assues that there are no pending requets with this driver */
|
|
+ driver->unbind(&dev->gadget);
|
|
+ dev->gadget.dev.driver = NULL;
|
|
+ dev->driver = NULL;
|
|
+ dev->connected = 0;
|
|
+
|
|
+ /* set SD */
|
|
+ ioh_udc_set_disconnect(dev->regs);
|
|
+
|
|
+ IOH_DEBUG("usb_gadget_unregister_driver: %s: unregistered",
|
|
+ driver->driver.name);
|
|
+
|
|
+ IOH_DEBUG("usb_gadget_unregister_driver: exit");
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
|
|
+
|
|
+/* Completes request packet ... caller MUST hold lock */
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void complete_req(struct ioh_udc_ep *ep, struct
|
|
+ * ioh_udc_request *req, int status)
|
|
+ *@brief This API is invoked from the driver when processing of a
|
|
+ * request is complete
|
|
+ *@remarks The main functions performed by this function include:
|
|
+ - Delete the request from the endpoints request queue
|
|
+ - Update the requests status with the status passed
|
|
+ - Unmap the requests buffer
|
|
+ - Reset DMA mapping status
|
|
+ - Set endpoint halted status
|
|
+ - Invoke the requests completion handler specified by the gadget driver
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@param req Reference to the request structure
|
|
+ *@param status indicates the success/failure of completion
|
|
+ *@return none
|
|
+ */
|
|
+static void complete_req(struct ioh_udc_ep *ep, struct ioh_udc_request *req,
|
|
+ int status)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+ unsigned halted = ep->halted;
|
|
+
|
|
+ list_del_init(&req->queue);
|
|
+
|
|
+ /* set new status if pending */
|
|
+ if (req->req.status == -EINPROGRESS)
|
|
+ req->req.status = status;
|
|
+ else
|
|
+ status = req->req.status;
|
|
+
|
|
+
|
|
+ dev = ep->dev;
|
|
+ if (req->dma_mapped) {
|
|
+ if (ep->in) {
|
|
+ pci_unmap_single(dev->pdev, req->req.dma,
|
|
+ req->req.length,
|
|
+ PCI_DMA_TODEVICE);
|
|
+ } else {
|
|
+ pci_unmap_single(dev->pdev, req->req.dma,
|
|
+ req->req.length,
|
|
+ PCI_DMA_FROMDEVICE);
|
|
+ }
|
|
+ req->dma_mapped = 0;
|
|
+ req->req.dma = DMA_ADDR_INVALID;
|
|
+ }
|
|
+ ep->halted = 1;
|
|
+
|
|
+ IOH_DEBUG("complete %s req %p status %d len %u",
|
|
+ ep->ep.name, &req->req, status, req->req.length);
|
|
+ IOH_DEBUG("complete %s ioh-req 0x%08x req->queue 0x%08x",
|
|
+ ep->ep.name, (u32)req, (u32)(&(req->queue)));
|
|
+ spin_unlock(&dev->lock);
|
|
+ if (!ep->in)
|
|
+ ioh_udc_ep_clear_rrdy(ep->regs);
|
|
+
|
|
+ req->req.complete(&ep->ep, &req->req);
|
|
+
|
|
+ spin_lock(&dev->lock);
|
|
+ ep->halted = halted;
|
|
+}
|
|
+
|
|
+/* Empty request queue of an endpoint; caller holds spinlock */
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn void empty_req_queue(struct ioh_udc_ep *ep)
|
|
+ *@brief This API empties the request queue of an endpoint
|
|
+ *@remarks The following actions are performed:
|
|
+ * - Set endpoint halted status as 1 in ep->halted
|
|
+ * - For each request in the endpoints queue, invoke
|
|
+ * the completion handler specified by the gadget driver
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - complete_req
|
|
+ */
|
|
+void empty_req_queue(struct ioh_udc_ep *ep)
|
|
+{
|
|
+ struct ioh_udc_request *req;
|
|
+
|
|
+ ep->halted = 1;
|
|
+ while (!list_empty(&ep->queue)) {
|
|
+ req = list_entry(ep->queue.next, struct ioh_udc_request, queue);
|
|
+ IOH_DEBUG("empty_req_queue: complete_req ep%d%s", ep->num,
|
|
+ (ep->in ? "in" : "out"));
|
|
+ complete_req(ep, req, -ESHUTDOWN);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_ep_enable(struct usb_ep *usbep,
|
|
+ * const struct usb_endpoint_descriptor *desc)
|
|
+ *@brief This API enables the endpoint. It is called from gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If usbep or descriptor is NULL or the endpoint is 0,
|
|
+ * return error -EINVAL
|
|
+ * - If packet size not specified in the descriptor,
|
|
+ * return -ERANGE
|
|
+ * - Configure the endpoint by invoking ioh_udc_ep_enable()
|
|
+ * - Enable Endpoint interrupts by invoking
|
|
+ * ioh_udc_enable_ep_interrupts
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param desc Reference to the USB endpoint descriptor structure
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_ep_enable
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ */
|
|
+static int ioh_udc_pcd_ep_enable(struct usb_ep *usbep,
|
|
+ const struct usb_endpoint_descriptor *desc)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ struct ioh_udc_dev *dev;
|
|
+ unsigned long iflags;
|
|
+
|
|
+ if ((usbep == NULL) || (usbep->name == ep0_string) || (desc == NULL) ||
|
|
+ (desc->bDescriptorType != USB_DT_ENDPOINT) ||
|
|
+ (desc->wMaxPacketSize == 0)) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ dev = ep->dev;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_ep_enable ep %d", ep->num);
|
|
+ if ((dev->driver == NULL) || (dev->gadget.speed == USB_SPEED_UNKNOWN))
|
|
+ return -ESHUTDOWN;
|
|
+
|
|
+
|
|
+ spin_lock_irqsave(&dev->lock, iflags);
|
|
+ ep->desc = desc;
|
|
+ ep->halted = 0;
|
|
+ ioh_udc_ep_enable(ep->regs, &ep->dev->cfg_data, desc);
|
|
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_ep_enable: %s enabled", usbep->name);
|
|
+
|
|
+ spin_unlock_irqrestore(&dev->lock, iflags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_ep_disable(struct usb_ep *usbep)
|
|
+ *@brief This API disables endpoint and is called from gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ *- If usbep or endpoints descriptor is NULL, return error -EINVAL
|
|
+ *- Empty request queue using empty_req_queue API
|
|
+ *- Un-configure the endpoint by invoking the HAL API ioh_udc_ep_disable API
|
|
+ *- Disable interrupts using ioh_udc_disable_ep_interrupts API
|
|
+ *- Set usb endpoint descriptor for the endpoint to NULL
|
|
+ *- Set ops method for endpoint request to ioh_udc_ep_ops
|
|
+ *- Initialize endpoint queue head using INIT_LIST_HEAD API
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - empty_req_queue
|
|
+ * - ioh_udc_ep_disable
|
|
+ * - ioh_udc_disable_ep_interrupts
|
|
+ */
|
|
+static int ioh_udc_pcd_ep_disable(struct usb_ep *usbep)
|
|
+{
|
|
+ struct ioh_udc_ep *ep = NULL;
|
|
+ unsigned long iflags;
|
|
+
|
|
+ if (usbep == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ if ((usbep->name == ep0_string) || (ep->desc == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_ep_disable: ep%d%s", ep->num,
|
|
+ (ep->in ? "in" : "out"));
|
|
+ spin_lock_irqsave(&ep->dev->lock, iflags);
|
|
+ empty_req_queue(ep);
|
|
+ ep->halted = 1;
|
|
+ ioh_udc_ep_disable(ep->regs);
|
|
+
|
|
+ /* disable interrupt */
|
|
+ ioh_udc_disable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+ ep->desc = NULL;
|
|
+ ep->ep.ops = &ioh_udc_ep_ops;
|
|
+ INIT_LIST_HEAD(&ep->queue);
|
|
+
|
|
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static struct usb_request *ioh_udc_alloc_request
|
|
+ * (struct usb_ep *usbep, gfp_t gfp)
|
|
+ *@brief This function allocates request structure. It iscalled by gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ *- If usbep is NULL, return NULL
|
|
+ *- Allocate and initialize memory for request structure
|
|
+ *- If allocation fails, return NULL
|
|
+ *- Initialize the linked list usbep->queue
|
|
+ *- If DMA mode is enabled, create data pool for EP0 IN requests. If it fails,
|
|
+ * release memory allocated for request structure
|
|
+ *- Set DMA descriptor status as HOST BUSY to prevent its usage
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param gfp Flag to be used while allocating memory
|
|
+ *@return struct usb_request *
|
|
+ * [ NULL on failure and allocated address on success ]
|
|
+ *@see
|
|
+ * - pci_pool_alloc
|
|
+ */
|
|
+static struct usb_request *ioh_udc_alloc_request(struct usb_ep *usbep,
|
|
+ gfp_t gfp)
|
|
+{
|
|
+ struct ioh_udc_request *req;
|
|
+ struct ioh_udc_ep *ep;
|
|
+
|
|
+ if (usbep == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ IOH_DEBUG("ioh_udc_alloc_request: ep %s", usbep->name);
|
|
+ req = kzalloc(sizeof(struct ioh_udc_request), gfp);
|
|
+ if (req == NULL) {
|
|
+ IOH_DEBUG("ioh_udc_alloc_request: no memory for request");
|
|
+ return NULL;
|
|
+ }
|
|
+ memset(req, 0, sizeof(struct ioh_udc_request));
|
|
+ req->req.dma = DMA_ADDR_INVALID;
|
|
+ INIT_LIST_HEAD(&req->queue);
|
|
+
|
|
+ if (ep->dma != NULL) {
|
|
+ struct ioh_udc_data_dma_desc *dma_desc;
|
|
+
|
|
+ /* ep0 in requests are allocated from data pool here */
|
|
+ dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
|
|
+ &req->td_data_phys);
|
|
+ if (NULL == dma_desc) {
|
|
+ kfree(req);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_alloc_request: req = 0x%p dma_desc = 0x%p, "
|
|
+ "td_phys = 0x%08lx",
|
|
+ req, dma_desc, (unsigned long)req->td_data_phys);
|
|
+
|
|
+ /* prevent from using desc. - set HOST BUSY */
|
|
+ dma_desc->status |= IOH_UDC_BS_HST_BSY;
|
|
+ dma_desc->dataptr = __constant_cpu_to_le32(DMA_ADDR_INVALID);
|
|
+ req->td_data = dma_desc;
|
|
+ req->td_data_last = dma_desc;
|
|
+ req->chain_len = 1;
|
|
+ }
|
|
+
|
|
+ return &req->req;
|
|
+}
|
|
+
|
|
+/* frees pci pool descriptors of a DMA chain */
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_free_dma_chain(struct ioh_udc_dev *dev,
|
|
+ * struct ioh_udc_request *req)
|
|
+ *@brief This function frees the DMA chain created for the request
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Get the virtual address of second DMA descriptor,
|
|
+ * of the chain using phys_to_virt
|
|
+ *- Release the allocated block back into PCI pool using pci_pool_free
|
|
+ *- Get the virtual address of next DMA descriptor,
|
|
+ * of the chain using phys_to_virt
|
|
+ *- Repeat steps 2, 3 for the chain length
|
|
+ *@param dev Reference to the driver structure
|
|
+ *@param req Reference to the request to be freed
|
|
+ *@return success
|
|
+ */
|
|
+static int ioh_udc_free_dma_chain(struct ioh_udc_dev *dev,
|
|
+ struct ioh_udc_request *req)
|
|
+{
|
|
+ int ret_val = 0;
|
|
+ struct ioh_udc_data_dma_desc *td;
|
|
+ struct ioh_udc_data_dma_desc *td_last = NULL;
|
|
+ unsigned int i;
|
|
+
|
|
+ /* 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;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_create_dma_chain(struct ioh_udc_ep *ep,
|
|
+ * struct ioh_udc_request *req, unsigned long buf_len, gfp_t gfp_flags)
|
|
+ *@brief This function creates or reinitializes a DMA chain
|
|
+ *@remarks The following actions are performed:
|
|
+ *- If the endpoint is output, set the first descriptor status as HOST_BUSY
|
|
+ *- Calculate the new chain length from the requested length and the maximum
|
|
+ * packet size available on the end point
|
|
+ *- Check if a shorter chain is already allocated (req->chain_len > 1).
|
|
+ * If TRUE, invoke ioh_udc_free_dma_chain to free the DMA chain.
|
|
+ *- Generate the required number of buffers and descriptors as follows:
|
|
+ *- Invoke pci_pool_alloc with the following arguments:
|
|
+ * DMA pool for data requests (ep->dev->data_requests),
|
|
+ * gfp_flags and a pointer to the DMA address.
|
|
+ *- If pci_pool_alloc fails, return -ENOMEM.
|
|
+ *- If the descriptor is valid, assign the DMA address corresponding
|
|
+ * to 'buf' in the current descriptors dataptr field (buffer descriptor).
|
|
+ *- Link the descriptor and assign the bytes to be transferred.
|
|
+ *- Set the byte count as well as the status (HOST_BUSY) for
|
|
+ * input endpoints. For Output endpoint, set the status alone.
|
|
+ *- For the last descriptor, set the last bit in the status field
|
|
+ * and update the next field to point to itself.
|
|
+ *- Return with IOH_UDC_SUCCESS
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@param req Reference to the request
|
|
+ *@param buf_len The buffer length
|
|
+ *@param gfp_flags Flags to be used while mapping the data buffer
|
|
+ *@return success, -ENOMEM (pci_pool_alloc invocation fails)
|
|
+ *@see
|
|
+ * - ioh_udc_free_dma_chain
|
|
+ */
|
|
+static int ioh_udc_create_dma_chain(struct ioh_udc_ep *ep,
|
|
+ struct ioh_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 ioh_udc_data_dma_desc *td = NULL;
|
|
+ struct ioh_udc_data_dma_desc *last = NULL;
|
|
+ unsigned long txbytes;
|
|
+ unsigned len;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_create_dma_chain: bytes = %ld buf_len = %ld",
|
|
+ bytes, buf_len);
|
|
+ /* unset L bit in first desc for OUT */
|
|
+ if (!ep->in)
|
|
+ req->td_data->status = IOH_UDC_BS_HST_BSY;
|
|
+
|
|
+
|
|
+ /* alloc only new desc's if not already available */
|
|
+ len = req->req.length / buf_len;
|
|
+ if (req->req.length % buf_len)
|
|
+ len++;
|
|
+
|
|
+ /* shorter chain already allocated before */
|
|
+ if (req->chain_len > 1)
|
|
+ ioh_udc_free_dma_chain(ep->dev, req);
|
|
+
|
|
+ req->chain_len = len;
|
|
+
|
|
+ td = req->td_data;
|
|
+ /* gen. required number of descriptors and buffers */
|
|
+ for (i = buf_len; i < bytes; i += buf_len) {
|
|
+ dma_addr = DMA_ADDR_INVALID;
|
|
+ /* create or determine next desc. */
|
|
+ td = pci_pool_alloc(ep->dev->data_requests, gfp_flags,
|
|
+ &dma_addr);
|
|
+ if (td == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ td->status = 0;
|
|
+ td->dataptr = req->req.dma + i; /* assign buffer */
|
|
+
|
|
+ if ((bytes - i) >= buf_len) {
|
|
+ txbytes = buf_len;
|
|
+ } else { /* short packet */
|
|
+ txbytes = bytes - i;
|
|
+ }
|
|
+ /* link td and assign tx bytes */
|
|
+ if (i == buf_len) {
|
|
+ req->td_data->next = dma_addr;
|
|
+ /* set the count bytes */
|
|
+ if (ep->in) {
|
|
+ req->td_data->status = IOH_UDC_BS_HST_BSY |
|
|
+ buf_len;
|
|
+ /* second desc */
|
|
+ td->status = IOH_UDC_BS_HST_BSY | txbytes;
|
|
+ } else {
|
|
+ td->status = IOH_UDC_BS_HST_BSY;
|
|
+ }
|
|
+ } else {
|
|
+ last->next = dma_addr;
|
|
+ if (ep->in)
|
|
+ td->status = IOH_UDC_BS_HST_BSY | txbytes;
|
|
+ else
|
|
+ td->status = IOH_UDC_BS_HST_BSY;
|
|
+
|
|
+ }
|
|
+ last = td;
|
|
+ }
|
|
+ /* set last bit */
|
|
+ if (td) {
|
|
+ td->status |= IOH_UDC_DMA_LAST;
|
|
+ /* last desc. points to itself */
|
|
+ req->td_data_last = td;
|
|
+ td->next = req->td_data_phys;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_free_request(struct usb_ep *usbep,
|
|
+ * struct usb_request *usbreq)
|
|
+ *@brief This function frees request structure. It is called by gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If any of the argument is NULL, return
|
|
+ * - Warn if req->queue is empty
|
|
+ * - Otherwise, if the chain length is greater than 1,
|
|
+ * invoke ioh_udc_free_dma_chain to free the DMA chain.
|
|
+ * - If the chain length is less than 1,
|
|
+ * release memory allocated for request structure and data pool
|
|
+ * by invoking pci_pool_free
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param usbreq Reference to the USB request
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_free_dma_chain
|
|
+ */
|
|
+static void ioh_udc_free_request(struct usb_ep *usbep,
|
|
+ struct usb_request *usbreq)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ struct ioh_udc_request *req;
|
|
+
|
|
+ if ((usbep == NULL) || (usbreq == NULL))
|
|
+ return;
|
|
+
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ req = container_of(usbreq, struct ioh_udc_request, req);
|
|
+ IOH_DEBUG("ioh_udc_free_request: %s req = 0x%p", usbep->name, req);
|
|
+
|
|
+ if (!list_empty(&req->queue))
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_free_request: %s req = 0x%p\
|
|
+ queue not empty", usbep->name, req);
|
|
+
|
|
+ if (req->td_data != NULL) {
|
|
+ if (req->chain_len > 1)
|
|
+ ioh_udc_free_dma_chain(ep->dev, req);
|
|
+ else
|
|
+ pci_pool_free(ep->dev->data_requests, req->td_data,
|
|
+ req->td_data_phys);
|
|
+
|
|
+ }
|
|
+ kfree(req);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static int prepare_dma(struct ioh_udc_ep *ep,
|
|
+ * struct ioh_udc_request *req, gfp_t gfp)
|
|
+ *@brief This function creates and initializes the DMA chain for the request
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Set buffer pointer in req->td_data->dataptr and last descriptor
|
|
+ * indication status in req->td_data->status.
|
|
+ *- Allocate and create a DMA chain using ioh_udc_create_dma_chain.
|
|
+ *- If the allocation fails, return -ENOMEM.
|
|
+ *- If the request is on an IN endpoint, update the count in the first
|
|
+ *descriptors status and mark the status (req->td_data->status) as host busy
|
|
+ * - Return success
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@param req Reference to the request
|
|
+ *@param gfp Flag to be used while mapping the data buffer
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_create_dma_chain
|
|
+ */
|
|
+static int prepare_dma(struct ioh_udc_ep *ep, struct ioh_udc_request *req,
|
|
+ gfp_t gfp)
|
|
+{
|
|
+ int retval = 0;
|
|
+ IOH_DEBUG("prepare_dma: enter req->req.dma = 0x%08x", req->req.dma);
|
|
+
|
|
+ /* set buffer pointer */
|
|
+ req->td_data->dataptr = req->req.dma;
|
|
+ /* set last bit */
|
|
+ req->td_data->status |= IOH_UDC_DMA_LAST;
|
|
+
|
|
+ /* Allocate and create a DMA chain */
|
|
+ retval = ioh_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
|
|
+ if (retval != 0) {
|
|
+ if (retval == -ENOMEM)
|
|
+ IOH_LOG(KERN_ERR, "prepare_dma: Out of DMA memory");
|
|
+
|
|
+ return retval;
|
|
+ }
|
|
+ if (ep->in) {
|
|
+ if (req->req.length <= ep->ep.maxpacket) {
|
|
+ /* write tx bytes */
|
|
+ req->td_data->status = IOH_UDC_DMA_LAST |
|
|
+ IOH_UDC_BS_HST_BSY | req->req.length;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ep->in) {
|
|
+ /* if bytes < max packet then tx bytes must
|
|
+ *be written in packet per buffer mode
|
|
+ */
|
|
+ if ((req->req.length < ep->ep.maxpacket) || (ep->num == 0)) {
|
|
+ /* write the count */
|
|
+ req->td_data->status = (req->td_data->status &
|
|
+ ~IOH_UDC_RXTX_BYTES) |
|
|
+ req->req.length;
|
|
+ }
|
|
+ /* set HOST BUSY */
|
|
+ req->td_data->status = (req->td_data->status &
|
|
+ ~IOH_UDC_BUFF_STS) |
|
|
+ IOH_UDC_BS_HST_BSY;
|
|
+ }
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void process_zlp(struct ioh_udc_ep *ep,
|
|
+ * struct ioh_udc_request *req)
|
|
+ *@brief This function process zero length packets from the gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Invoke the requests completion routine
|
|
+ *- If there is a set config or set interface request that is not
|
|
+ * acknowledged, set CSR_DONE using ioh_udc_set_csr_done API.
|
|
+ * Reset dev->set_cfg_not_acked to 0.
|
|
+ *- If there is a setup request waiting for acknowledgement,
|
|
+ *clear the NAK on EP0 IN using ioh_udc_ep_clear_nak API and set the
|
|
+ *naking status as 0 in dev->ep[UDC_EP0IN_IDX].naking. Reset dev->
|
|
+ * waiting_zlp_ack to 0
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@param req Reference to the request
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - complete_req
|
|
+ * - ioh_udc_set_csr_done
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ */
|
|
+static void process_zlp(struct ioh_udc_ep *ep, struct ioh_udc_request *req)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = ep->dev;
|
|
+
|
|
+ IOH_DEBUG("process_zlp: enter ep%d%s",
|
|
+ ep->num, (ep->in ? "in" : "out"));
|
|
+ /* IN zlp's are handled by hardware */
|
|
+ complete_req(ep, req, 0);
|
|
+
|
|
+ /* if set_config or set_intf is waiting for ack by zlp
|
|
+ *then set CSR_DONE
|
|
+ */
|
|
+ if (dev->set_cfg_not_acked) {
|
|
+ IOH_DEBUG("process_zlp: csr done");
|
|
+ ioh_udc_set_csr_done(dev->regs);
|
|
+ dev->set_cfg_not_acked = 0;
|
|
+ }
|
|
+ /* setup command is ACK'ed now by zlp */
|
|
+ if (!dev->stall) {
|
|
+ if (dev->waiting_zlp_ack) {
|
|
+ /* clear NAK by writing CNAK in EP0_IN */
|
|
+ ioh_udc_ep_clear_nak(dev->ep[UDC_EP0IN_IDX].regs);
|
|
+ dev->waiting_zlp_ack = 0;
|
|
+ }
|
|
+ }
|
|
+ IOH_DEBUG("process_zlp: exit");
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_queue(struct usb_ep *usbep,
|
|
+ * struct usb_request *usbreq, gfp_t gfp)
|
|
+ *@brief This function queues a request packet. It is called by gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If any of the argument is NULL or the request has NULL buffer
|
|
+ * or NULL completion handler, return -EINVAL
|
|
+ * - Verify if the endpoint is enabled before trying to queue. if not,
|
|
+ * return -EINVAL
|
|
+ * - Verify if the request queue is non empty. If so, return -EINVAL
|
|
+ * - If the gadget is not bound or invalid, return -ESHUTDOWN
|
|
+ * - Setup a DMA mapping for the request buffer and
|
|
+ * allocate the DMA descriptors
|
|
+ * - If usbeps request queue is empty
|
|
+ * - If length of data equals 0, call process_zlp API
|
|
+ * and return IOH_UDC_SUCCESS
|
|
+ * - if there are no IN endpoints,
|
|
+ * - Start the receive request by invoking
|
|
+ * ioh_udc_start_rxrequest
|
|
+ * - Oterwise
|
|
+ * - Wait till STALL bit in endpoint control register
|
|
+ * is cleared
|
|
+ * - Clear NAK by invoking ioh_udc_ep_clear_nak.
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ * - Enable TX DMA by invoking ioh_udc_set_dma
|
|
+ * - If request queue not empty,
|
|
+ * - Add the request to end of usbeps queue
|
|
+ * - Return with IOH_UDC_SUCCESS
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param usbreq Reference to the USB request
|
|
+ *@param gfp Flag to be used while mapping the data buffer
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - prepare_dma
|
|
+ * - process_zlp
|
|
+ * - ioh_udc_start_rxrequest
|
|
+ * - ioh_udc_read_ep_control
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_set_dma
|
|
+ */
|
|
+static int ioh_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
|
|
+ gfp_t gfp)
|
|
+{
|
|
+ int retval = 0;
|
|
+ struct ioh_udc_ep *ep;
|
|
+ struct ioh_udc_dev *dev;
|
|
+ struct ioh_udc_request *req;
|
|
+ unsigned long iflags;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_queue: enter");
|
|
+ if ((usbep == NULL) || (usbreq == NULL) || (usbreq->complete == NULL) ||
|
|
+ (usbreq->buf == NULL)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_queue: Invalid end point OR request");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ if ((ep->desc == NULL) && (ep->num != 0)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_queue: Trying to queue before before\
|
|
+ enabling the end point %d", ep->num);
|
|
+ /* Don't let non-control ep queue before enable */
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ req = container_of(usbreq, struct ioh_udc_request, req);
|
|
+ IOH_DEBUG("ioh_udc_pcd_queue: ep%d%s req = 0x%08x", ep->num,
|
|
+ (ep->in ? "in" : "out"), (u32)req);
|
|
+ if (!list_empty(&req->queue)) {
|
|
+ IOH_DEBUG("list_empty error: req->queue = 0x%08x\
|
|
+ ioh-req = 0x%08x", (u32)(&(req->queue)), (u32)req);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ dev = ep->dev;
|
|
+ if ((dev->driver == NULL) || (dev->gadget.speed == USB_SPEED_UNKNOWN)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_queue: Gadget not bound/invalid");
|
|
+ IOH_DEBUG("dev->driver = 0x%p speed = 0x%x", dev->driver,
|
|
+ dev->gadget.speed);
|
|
+ return -ESHUTDOWN;
|
|
+ }
|
|
+ spin_lock_irqsave(&ep->dev->lock, iflags);
|
|
+ /* map the buffer for dma */
|
|
+ if ((usbreq->length != 0) &&
|
|
+ ((usbreq->dma == DMA_ADDR_INVALID) || (usbreq->dma == 0))) {
|
|
+ if (ep->in) {
|
|
+ usbreq->dma = pci_map_single(dev->pdev, usbreq->buf,
|
|
+ usbreq->length, PCI_DMA_TODEVICE);
|
|
+ } else {
|
|
+ usbreq->dma = pci_map_single(dev->pdev, usbreq->buf,
|
|
+ usbreq->length, PCI_DMA_FROMDEVICE);
|
|
+ }
|
|
+ req->dma_mapped = 1;
|
|
+ }
|
|
+
|
|
+ if (usbreq->length > 0) { /* setup the descriptors */
|
|
+ retval = prepare_dma(ep, req, gfp);
|
|
+ if (retval != 0) {
|
|
+ /* Need to unmap before returning? ...
|
|
+ req->dma_mapped = 1; */
|
|
+ spin_unlock_irqrestore(&dev->lock, iflags);
|
|
+ return retval;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ usbreq->actual = 0;
|
|
+ usbreq->status = -EINPROGRESS;
|
|
+ req->dma_done = 0;
|
|
+
|
|
+ if (list_empty(&ep->queue) && !ep->halted) {
|
|
+ /* no pending transfer, so start this req */
|
|
+ if ((usbreq->length == 0)) {
|
|
+ process_zlp(ep, req);
|
|
+ spin_unlock_irqrestore(&dev->lock, iflags);
|
|
+ return 0;
|
|
+ }
|
|
+ if (!ep->in) {
|
|
+ ioh_udc_start_rxrequest(ep, req);
|
|
+ } else {
|
|
+ /*
|
|
+ * For IN trfr the descriptors will be programmed and
|
|
+ * P bit will be set when
|
|
+ * we get an IN token
|
|
+ */
|
|
+
|
|
+ while (ioh_udc_read_ep_control(ep->regs) &
|
|
+ (1 << UDC_EPCTL_S))
|
|
+ udelay(100);
|
|
+
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ (1 << ep->num));
|
|
+ /* enable DMA */
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_TX);
|
|
+ }
|
|
+ }
|
|
+ IOH_DEBUG("ioh_udc_pcd_queue: desc[stat:0x%08x dptr:0x%08x\
|
|
+ next:0x%08x]",
|
|
+ req->td_data->status, req->td_data->dataptr,
|
|
+ req->td_data->next);
|
|
+ /* Now add this request to the ep's pending requests */
|
|
+ if (req != NULL)
|
|
+ list_add_tail(&req->queue, &ep->queue);
|
|
+
|
|
+#ifdef IOH_PRINT_REG
|
|
+ ioh_udc_print_regs((u32)dev->virt_addr);
|
|
+#endif
|
|
+ spin_unlock_irqrestore(&dev->lock, iflags);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_dequeue(struct usb_ep *usbep,
|
|
+ * struct usb_request *usbreq)
|
|
+ *@brief This function de-queues a request packet. It is called
|
|
+ * by gadget driver
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If any of the argument is NULL, return -EINVAL
|
|
+ * - If the request is not in usbeps queue, return -EINVAL
|
|
+ * - Set the NAK bit
|
|
+ * - If the request is not in processing , call its\
|
|
+ * completion handler
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param usbreq Reference to the USB request
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_ep_set_nak
|
|
+ * - complete_req
|
|
+ */
|
|
+static int ioh_udc_pcd_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ struct ioh_udc_request *req;
|
|
+ unsigned long flags;
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ if ((usbep == NULL) || (usbreq == NULL) ||
|
|
+ ((ep->desc == NULL) && (ep->num != 0))) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ IOH_DEBUG("ioh_udc_pcd_dequeue: enter ep%d%s", ep->num,
|
|
+ (ep->in ? "in" : "out"));
|
|
+ req = container_of(usbreq, struct ioh_udc_request, req);
|
|
+ spin_lock_irqsave(&ep->dev->lock, flags);
|
|
+ /* make sure it's still queued on this endpoint */
|
|
+ list_for_each_entry(req, &ep->queue, queue) {
|
|
+ if (&req->req == usbreq)
|
|
+ break;
|
|
+
|
|
+ }
|
|
+
|
|
+ if (&req->req != usbreq) {
|
|
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ioh_udc_ep_set_nak(ep->regs);
|
|
+ if (!list_empty(&req->queue))
|
|
+ complete_req(ep, req, -ECONNRESET);
|
|
+
|
|
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
|
|
+ *@brief This function Sets or clear the endpoint halt feature
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the ep->desc is NULL and ep->num is 0,
|
|
+ * return -EINVAL.
|
|
+ * - If ep->dev->driver is NULL and speed is unknown,
|
|
+ * return -ESHUTDOWN
|
|
+ * - If there are pending transfers, return -EAGAIN
|
|
+ * - If halt is 0, Clear the STALL for the endpoint
|
|
+ * - Otherwise,
|
|
+ * - Set the STALL for the endpoint
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param halt Specifies whether to set or clear the feature
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ *@see
|
|
+ * - ioh_udc_ep_clear_stall
|
|
+ * - ioh_udc_ep_set_stall
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ */
|
|
+static int ioh_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ unsigned long iflags;
|
|
+
|
|
+ if (usbep == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_halt: %s: halt=%d", usbep->name, halt);
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+
|
|
+ if ((ep->desc == NULL) && (ep->num == 0)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_halt: ep->desc = 0x%x:\
|
|
+ ep->num = 0x%x", (u32)(ep->desc), ep->num);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if ((ep->dev->driver == NULL) || (ep->dev->gadget.speed\
|
|
+ == USB_SPEED_UNKNOWN)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_halt: ep->dev->driver = 0x%x:\
|
|
+ ep->dev->gadget.speed = 0x%x", (u32)(ep->dev->driver),
|
|
+ ep->dev->gadget.speed);
|
|
+ return -ESHUTDOWN;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
|
|
+
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_halt: list not empty");
|
|
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+ /* halt or clear halt */
|
|
+ if (halt == 0) {
|
|
+ ioh_udc_ep_clear_stall(ep->regs);
|
|
+ } else {
|
|
+ if (ep->num == IOH_UDC_EP0)
|
|
+ ep->dev->stall = 1;
|
|
+
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+ }
|
|
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static int ioh_udc_pcd_set_wedge(struct usb_ep *usbep)
|
|
+ *@brief This function Sets or clear the endpoint halt feature
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@param halt Specifies whether to set or clear the feature
|
|
+ *@return int [ 0 on success and linux error number on failure ]
|
|
+ */
|
|
+static int ioh_udc_pcd_set_wedge(struct usb_ep *usbep)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ unsigned long iflags;
|
|
+
|
|
+ if (usbep == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_wedge: %s:", usbep->name);
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+
|
|
+ if ((ep->desc == NULL) && (ep->num == 0)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_wedge: ep->desc = 0x%x:\
|
|
+ ep->num = 0x%x", (u32)(ep->desc), ep->num);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if ((ep->dev->driver == NULL) || (ep->dev->gadget.speed ==\
|
|
+ USB_SPEED_UNKNOWN)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_wedge: ep->dev->driver = 0x%x:\
|
|
+ ep->dev->gadget.speed = 0x%x", (u32)(ep->dev->driver),
|
|
+ ep->dev->gadget.speed);
|
|
+ return -ESHUTDOWN;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
|
|
+
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ IOH_DEBUG("ioh_udc_pcd_set_wedge: list not empty");
|
|
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+ /* halt */
|
|
+ if (ep->num == IOH_UDC_EP0)
|
|
+ ep->dev->stall = 1;
|
|
+
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+
|
|
+ ep->dev->prot_stall = 1;
|
|
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_pcd_fifo_flush(struct usb_ep *usbep)
|
|
+ *@brief This function Flush the FIFO of specified endpoint
|
|
+ *@remarks The following actions are performed:
|
|
+ * - Depending on the endpoint direction, flush the TX or RX FIFO
|
|
+ *@param usbep Reference to the USB endpoint structure
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - ioh_udc_ep_fifo_flush
|
|
+ */
|
|
+static void ioh_udc_pcd_fifo_flush(struct usb_ep *usbep)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+
|
|
+ if (usbep == NULL)
|
|
+ return;
|
|
+
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_pcd_fifo_flush: %s", usbep->name);
|
|
+
|
|
+ ep = container_of(usbep, struct ioh_udc_ep, ep);
|
|
+ if ((ep->desc == NULL) && (ep->num != 0))
|
|
+ return;
|
|
+
|
|
+
|
|
+ ioh_udc_ep_fifo_flush(ep->regs, ep->in);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void ioh_udc_svc_data_out(struct ioh_udc_dev *dev,
|
|
+ * int ep_num)
|
|
+ *@brief Handles interrupts from OUT endpoint
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Depending on the endpoint direction, flush the TX or RX FIFO
|
|
+ *- If the interrupt is BNA and request queue is not empty, get next request
|
|
+ * - If DMA state is DONE and dma_going flag is not set, start receive
|
|
+ * request.
|
|
+ *- If the interrupt is HE, log the error message
|
|
+ *- If the interrupt is RSS,
|
|
+ * - Set STALL useig ioh_udc_ep_set_stall API.
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ *- If the interrupt is RCS,
|
|
+ * - If the protocol stall flag not set,
|
|
+ * - Clear stall useing ioh_udc_ep_clear_stall API.
|
|
+ * - Otherwise
|
|
+ * - Set STALL using ioh_udc_ep_set_stall API.
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ *- If the interrupt is OUT_DATA,
|
|
+ * - If the protocol stall flag is set,
|
|
+ * - Set STALL using ioh_udc_ep_set_stall API.
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ * - Otherwise
|
|
+ * - invoke the completion routine using ioh_udc_complete_receiver
|
|
+ *- If request queue is empty,
|
|
+ * - Enable DMA using ioh_udc_set_dma API
|
|
+ *@param dev Reference to the device structure
|
|
+ *@param ep_num Endpoint that generated the interrupt
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - ioh_udc_start_rxrequest
|
|
+ * - ioh_udc_ep_set_stall
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_ep_clear_stall
|
|
+ * - ioh_udc_complete_receiver
|
|
+ * - ioh_udc_set_dma
|
|
+ */
|
|
+static void ioh_udc_svc_data_out(struct ioh_udc_dev *dev, int ep_num)
|
|
+{
|
|
+ u32 epsts;
|
|
+ struct ioh_udc_ep *ep;
|
|
+ struct ioh_udc_request *req = NULL;
|
|
+
|
|
+ ep = &dev->ep[2*ep_num + 1];
|
|
+ epsts = ep->epsts;
|
|
+ ep->epsts = 0;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_svc_data_out: ep%d%s status = 0x%08x", ep->num,
|
|
+ (ep->in ? "in" : "out"), epsts);
|
|
+ if (epsts & (1 << UDC_EPSTS_BNA)) { /* Just log it; only in DMA mode */
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ /* next request */
|
|
+ req = list_entry(ep->queue.next, struct ioh_udc_request,
|
|
+ queue);
|
|
+ IOH_DEBUG("BNA on ep%dout occured", ep->num);
|
|
+ if ((req->td_data_last->status & IOH_UDC_BUFF_STS) !=
|
|
+ IOH_UDC_BS_DMA_DONE) {
|
|
+ if (req->dma_going == 0)
|
|
+ ioh_udc_start_rxrequest(ep, req);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_HE)) { /* Host error - Just log it */
|
|
+ IOH_DEBUG("Host Error on ep%dout occured", ep->num);
|
|
+ return;
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_RSS)) {
|
|
+ IOH_DEBUG("ioh_udc_svc_data_out: RSS");
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_RCS)) {
|
|
+ IOH_DEBUG("ioh_udc_svc_data_out: RCS prot_stall = %d",
|
|
+ dev->prot_stall);
|
|
+ if (dev->prot_stall == 0) {
|
|
+ ioh_udc_ep_clear_stall(ep->regs);
|
|
+ } else {
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num +
|
|
+ UDC_EPINT_OUT_EP0));
|
|
+ }
|
|
+ }
|
|
+ if (((epsts & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_OFS) ==
|
|
+ UDC_EPSTS_OUT_DATA) {
|
|
+ if (ep->dev->prot_stall == 1) {
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num +
|
|
+ UDC_EPINT_OUT_EP0));
|
|
+ } else {
|
|
+ ioh_udc_complete_receiver(ep);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (list_empty(&ep->queue)) {
|
|
+ /* enable DMA */
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_RX);
|
|
+ }
|
|
+}
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void ioh_udc_svc_control_in(struct ioh_udc_dev *dev)
|
|
+ *@brief Handle Control IN endpoint interrupts
|
|
+ *@remarks The following actions are performed:
|
|
+ * - if the interrupt is TDC [DMA completed] or XFERDONE or TXEMPTY
|
|
+ * or ISOINDONE or HE or BNA, log the status.
|
|
+ * - If there is an TDC token
|
|
+ * - Invoke the requests completion routine.
|
|
+ * - If there is an IN token
|
|
+ * - Invoke the next request start routine.
|
|
+ *@param dev Reference to the device structure
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - ioh_udc_complete_transfer
|
|
+ * - ioh_udc_start_next_txrequest
|
|
+ */
|
|
+static void ioh_udc_svc_control_in(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ u32 epsts;
|
|
+ struct ioh_udc_ep *ep;
|
|
+
|
|
+ ep = &dev->ep[UDC_EP0IN_IDX];
|
|
+ epsts = ep->epsts;
|
|
+ ep->epsts = 0;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_ep0_in intr status 0x%x", epsts);
|
|
+
|
|
+ if ((epsts & ((1 << UDC_EPSTS_IN) | (1 << UDC_EPSTS_BNA) |
|
|
+ (1 << UDC_EPSTS_HE) |
|
|
+ (1 << UDC_EPSTS_TDC) | (1 << UDC_EPSTS_RCS) |
|
|
+ (1 << UDC_EPSTS_TXEMPTY) |
|
|
+ (1 << UDC_EPSTS_XFERDONE))) == 0) {
|
|
+ IOH_DEBUG("Non interrupt request ep%din status %x",
|
|
+ ep->num, epsts);
|
|
+ return;
|
|
+ }
|
|
+ if ((epsts & (1 << UDC_EPSTS_BNA))) { /* Just log it */
|
|
+ IOH_DEBUG("BNA on ep%din occured", ep->num);
|
|
+ return;
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_HE)) {
|
|
+ IOH_DEBUG("Host Error on ep%din occured", ep->num);
|
|
+ return;
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_TXEMPTY)) { /* Tx empty */
|
|
+ IOH_DEBUG("ioh_udc_ep0_in intr: TXEMPTY");
|
|
+ }
|
|
+ if ((epsts & (1 << UDC_EPSTS_TDC)) && (!dev->stall)) {
|
|
+ /* DMA completed */
|
|
+ IOH_DEBUG("TDC on ep%din", ep->num);
|
|
+ ioh_udc_complete_transfer(ep);
|
|
+ }
|
|
+ /* On IN interrupt, provide data if we have any */
|
|
+ if ((epsts & (1 << UDC_EPSTS_IN)) &&
|
|
+ ((epsts & (1 << UDC_EPSTS_TDC)) == 0) &&
|
|
+ ((epsts & (1 << UDC_EPSTS_TXEMPTY)) == 0)) {
|
|
+ ioh_udc_start_next_txrequest(ep);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void ioh_udc_svc_control_out(struct ioh_udc_dev *dev)
|
|
+ *@brief Routine that handle Control OUT endpoint interrupts
|
|
+ *@param dev Reference to the device structure
|
|
+ *@return none
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If Buffer NOT Available, log a debug message
|
|
+ * - If setup data present
|
|
+ *- Set device stall status to False
|
|
+ *- Invoke ioh_udc_ep_set_nak API to set SNAK field of IN endpoint register
|
|
+ *- Get the data from setup data descriptor
|
|
+ * Initialize setup data descriptor using ioh_udc_fifo_read API
|
|
+ *- Disable the TX DMA using ioh_udc_clear_dma API
|
|
+ *- Make the gadget's ep0 point to appropriate endpoint
|
|
+ * structure (ep0 IN or OUT)
|
|
+ * based on the request type
|
|
+ *- If the receive data is "Mass storage Reset", clear the protocol stall flag.
|
|
+ *- Invoke the gadget driver's setup routine with data received
|
|
+ *- if the setup routine returns a value between 0 and maximum packet size
|
|
+ * (ep0 in returns data on IN phase),
|
|
+ * - Invoke ioh_udc_ep_clear_nak API to clear NAK for In endpoint 0.
|
|
+ * - Enable DMA for RX using ioh_udc_set_dma API.
|
|
+ * - Invoke ioh_udc_ep_clear_nak API to clear NAK for In endpoint 0.
|
|
+ *- If the setup routine fails (returns < 0),
|
|
+ * - Stall the device using ioh_udc_ep_set_stall API
|
|
+ * - Enable the interrupt endpoint
|
|
+ * - Enable DMA for RX using ioh_udc_set_dma API.
|
|
+ *- Otherwise
|
|
+ * - Set 'waiting for zlp ACK' status as TRUE
|
|
+ *- If data is present
|
|
+ *- If request queue is empty,
|
|
+ * - Set the descriptor status as HOST_BUSY
|
|
+ * - Enable DMA for RX using ioh_udc_set_dma API.
|
|
+ *- Otherwise
|
|
+ * - Invoke ioh_udc_svc_data_out to handle the request.
|
|
+ * - Re-program the data descriptor using ioh_udc_ep_set_ddptr API
|
|
+ * - Enable Rx DMA using ioh_udc_set_dma API
|
|
+ *@see
|
|
+ * - ioh_udc_ep_set_nak
|
|
+ * - ioh_udc_init_setup_buff
|
|
+ * - ioh_udc_clear_dma
|
|
+ * - ioh_udc_ep_fifo_flush
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ * - ioh_udc_set_dma
|
|
+ * - ioh_udc_ep_set_stall
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_svc_data_out
|
|
+ * - ioh_udc_ep_set_ddptr
|
|
+ * - ioh_udc_ep_set_rrdy
|
|
+ */
|
|
+static void ioh_udc_svc_control_out(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ u32 stat;
|
|
+ int setup_supported;
|
|
+ struct ioh_udc_ep *ep;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_ep0_out");
|
|
+ ep = &dev->ep[UDC_EP0OUT_IDX];
|
|
+ stat = ep->epsts;
|
|
+ ep->epsts = 0;
|
|
+
|
|
+ if (stat & (1 << UDC_EPSTS_BNA)) {
|
|
+ IOH_DEBUG("EP0: BNA");
|
|
+ /* When we get a request, we will populate the descriptors. */
|
|
+ /* Anything else to do? */
|
|
+ }
|
|
+ /* If setup data */
|
|
+ if (((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_OFS) ==
|
|
+ UDC_EPSTS_OUT_SETUP) {
|
|
+ dev->stall = 0;
|
|
+ dev->ep[UDC_EP0IN_IDX].halted = 0;
|
|
+ dev->ep[UDC_EP0OUT_IDX].halted = 0;
|
|
+ /* In data not ready */
|
|
+ ioh_udc_ep_set_nak(dev->ep[UDC_EP0IN_IDX].regs);
|
|
+ setup_data.data[0] = ep->td_stp->data12;
|
|
+ setup_data.data[1] = ep->td_stp->data34;
|
|
+ IOH_DEBUG("EP0 setup data12: 0x%x data34:0x%x",
|
|
+ ep->td_stp->data12, ep->td_stp->data34);
|
|
+ ioh_udc_init_setup_buff(ep->td_stp);
|
|
+ ioh_udc_clear_dma(dev->regs, DMA_DIR_TX);
|
|
+ ioh_udc_ep_fifo_flush(dev->ep[UDC_EP0IN_IDX].regs,
|
|
+ dev->ep[UDC_EP0IN_IDX].in);
|
|
+ if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) {
|
|
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
|
|
+ } else { /* OUT */
|
|
+ dev->gadget.ep0 = &ep->ep;
|
|
+ }
|
|
+ IOH_DEBUG("EP0 setup data: 0x%x 0x%x", setup_data.data[0],
|
|
+ setup_data.data[1]);
|
|
+ spin_unlock(&dev->lock);
|
|
+ /* Mass storage Reset */
|
|
+ if ((setup_data.data[0] == 0x0000ff21) && (setup_data.data[1] ==
|
|
+ 0x00000000)) {
|
|
+ dev->prot_stall = 0;
|
|
+ IOH_DEBUG("Mass storage reset prot_stall = %d",
|
|
+ dev->prot_stall);
|
|
+ }
|
|
+ /* call gadget with setup data received */
|
|
+ setup_supported = dev->driver->setup(&dev->gadget,
|
|
+ &setup_data.request);
|
|
+ spin_lock(&dev->lock);
|
|
+
|
|
+ /* ep0 in returns data on IN phase */
|
|
+ if (setup_supported >= 0 && setup_supported <
|
|
+ UDC_EP0IN_MAX_PKT_SIZE) {
|
|
+ ioh_udc_ep_clear_nak(dev->ep[UDC_EP0IN_IDX].regs);
|
|
+ /* Gadget would have queued a request when
|
|
+ we called the setup */
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_RX);
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+ } else if (setup_supported < 0) {
|
|
+ /* if unsupported request, then stall */
|
|
+ IOH_DEBUG("EP0 setup unsupported: ep0_set_stall");
|
|
+ ioh_udc_ep_set_stall(dev->ep[UDC_EP0IN_IDX].regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num +
|
|
+ UDC_EPINT_OUT_EP0));
|
|
+ dev->stall = 0;
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_RX);
|
|
+ } else {
|
|
+ dev->waiting_zlp_ack = 1;
|
|
+ }
|
|
+ } else if ((((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_OFS) ==
|
|
+ UDC_EPSTS_OUT_DATA) && (dev->stall == 0)) {
|
|
+ if (list_empty(&ep->queue)) {
|
|
+ IOH_LOG(KERN_ERR, "control_out: ZLP");
|
|
+ /* If no requests, reactivate */
|
|
+ ep->td_data->status =
|
|
+ (ep->td_data->status &
|
|
+ ~IOH_UDC_BUFF_STS) |
|
|
+ IOH_UDC_BS_HST_RDY;
|
|
+ /* Enable RDE */
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_RX);
|
|
+ } else {
|
|
+ /* control write */
|
|
+ ioh_udc_svc_data_out(dev, UDC_EP0OUT_IDX);
|
|
+ /* re-program desc. pointer for possible ZLPs */
|
|
+ ioh_udc_ep_set_ddptr(ep->regs,
|
|
+ ep->td_data_phys);
|
|
+ /* Enable RDE */
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_RX);
|
|
+ }
|
|
+ }
|
|
+ ioh_udc_ep_set_rrdy(ep->regs);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void ioh_udc_svc_data_in(struct ioh_udc_dev *dev, int ep_num)
|
|
+ *@brief This function process endpoint interrupts for IN endpoints
|
|
+ *@param dev Reference to the device structure
|
|
+ *@param ep_num Endpoint that generated the interrupt
|
|
+ *@return none
|
|
+ *@remarks The interrupts are processed as follows:
|
|
+ *- If the interrupt is HE, log the error message
|
|
+ *- If the interrupt is RSS,
|
|
+ * - Set STALL useig ioh_udc_ep_set_stall API
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ *- If the interrupt is RCS,
|
|
+ * - If the protocol stall flag not set,
|
|
+ * - Clear stall useing ioh_udc_ep_clear_stall API
|
|
+ * - Otherwise
|
|
+ * - Set STALL using ioh_udc_ep_set_stall API.
|
|
+ * - Enable endpoint interrupts for out endpoint
|
|
+ *- If the interrupt is TDC,
|
|
+ * - Invoke the completion routine using ioh_udc_complete_receiver API
|
|
+ *- If the interrupt is IN, and is not RSS/TDC/TXEMPTY
|
|
+ * - Start the next transmit request using ioh_udc_start_next_txrequest API
|
|
+ *@see
|
|
+ * - ioh_udc_ep_set_stall
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_ep_clear_stall
|
|
+ * - ioh_udc_complete_transfer
|
|
+ * - ioh_udc_start_next_txrequest
|
|
+ */
|
|
+static void ioh_udc_svc_data_in(struct ioh_udc_dev *dev, int ep_num)
|
|
+{
|
|
+ u32 epsts;
|
|
+ struct ioh_udc_ep *ep;
|
|
+ ep = &dev->ep[2*ep_num];
|
|
+ epsts = ep->epsts;
|
|
+ ep->epsts = 0;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_svc_data_in: ep%d%s status = 0x%08x", ep->num,
|
|
+ (ep->in ? "in" : "out"), epsts);
|
|
+
|
|
+ if ((epsts & ((1 << UDC_EPSTS_IN) | (1 << UDC_EPSTS_BNA) |
|
|
+ (1 << UDC_EPSTS_HE) |
|
|
+ (1 << UDC_EPSTS_TDC) | (1 << UDC_EPSTS_RCS) |
|
|
+ (1 << UDC_EPSTS_TXEMPTY) |
|
|
+ (1 << UDC_EPSTS_RSS) |
|
|
+ (1 << UDC_EPSTS_XFERDONE))) == 0) {
|
|
+ IOH_DEBUG("Non interrupt request ep%din status %x",
|
|
+ ep->num, epsts);
|
|
+ return;
|
|
+ }
|
|
+ if ((epsts & (1 << UDC_EPSTS_BNA))) { /* Just log it */
|
|
+ IOH_DEBUG("BNA on ep%din occured", ep->num);
|
|
+ return;
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_HE)) {
|
|
+ IOH_DEBUG("Host Error on ep%din occured", ep->num);
|
|
+ return;
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_RSS)) {
|
|
+ IOH_DEBUG("ioh_udc_svc_data_in: RSS");
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_RCS)) {
|
|
+ IOH_DEBUG("ioh_udc_svc_data_in: RCS prot_stall = %d",
|
|
+ dev->prot_stall);
|
|
+ if (dev->prot_stall == 0) {
|
|
+ ioh_udc_ep_clear_stall(ep->regs);
|
|
+ } else {
|
|
+ ioh_udc_ep_set_stall(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num +
|
|
+ UDC_EPINT_OUT_EP0));
|
|
+ }
|
|
+ }
|
|
+ if (epsts & (1 << UDC_EPSTS_TDC)) { /* DMA completed */
|
|
+ ioh_udc_complete_transfer(ep);
|
|
+ }
|
|
+ /* On IN interrupt, provide data if we have any */
|
|
+ if ((epsts & (1 << UDC_EPSTS_IN)) &&
|
|
+ ((epsts & (1 << UDC_EPSTS_RSS)) == 0) &&
|
|
+ ((epsts & (1 << UDC_EPSTS_TDC)) == 0) &&
|
|
+ ((epsts & (1 << UDC_EPSTS_TXEMPTY)) == 0)) {
|
|
+ ioh_udc_start_next_txrequest(ep);
|
|
+ }
|
|
+ IOH_DEBUG("ioh_udc_ep_in intr ep ctrl = 0x%x",
|
|
+ IOH_READ32((u32)&ep->regs->epctl));
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_complete_transfer(struct ioh_udc_ep *ep)
|
|
+ *@brief This function completes a transfer
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@remarks The following actions are performed:
|
|
+ * - Get the request from the queue
|
|
+ * - If the request is valid, do the following
|
|
+ *- Get the last dma descriptor. if the DMA transfer is completed,
|
|
+ * - update the request length as actual
|
|
+ *- If the actual length has been updated,
|
|
+ * - update the status field of the last request
|
|
+ * - complete the request using complete_req function
|
|
+ * - set dma in progress status as false
|
|
+ * - if there are pending requests, clear NAK using ioh_udc_ep_clear_nak
|
|
+ * and enable
|
|
+ * endpoint interrupts
|
|
+ * - if there are no pending requests, set DMA in progress status
|
|
+ * as false and disable
|
|
+ * endpoint interrupts
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - complete_req
|
|
+ * - ioh_udc_read_ep_control
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_disable_ep_interrupts
|
|
+ */
|
|
+static void ioh_udc_complete_transfer(struct ioh_udc_ep *ep)
|
|
+{
|
|
+ struct ioh_udc_request *req;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_complete_transfer");
|
|
+
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ IOH_DEBUG("list_entry");
|
|
+ req = list_entry(ep->queue.next, struct ioh_udc_request, queue);
|
|
+ if (req && ((req->td_data_last->status & IOH_UDC_BUFF_STS) ==
|
|
+ IOH_UDC_BS_DMA_DONE)) {
|
|
+#ifdef DMA_PPB_WITH_DESC_UPDATE
|
|
+ struct ioh_udc_data_dma_desc *td_data = req->td_data;
|
|
+ for (i = 0; i < req->chain_len; i++) {
|
|
+ if ((td_data->status & IOH_UDC_RXTX_STS) !=
|
|
+ IOH_UDC_RTS_SUCC) {
|
|
+ IOH_LOG(KERN_ERR, "Invalid RXTX status\
|
|
+ (0x%08x) epstatus=0x%08x\n",
|
|
+ (td_data->status &
|
|
+ IOH_UDC_RXTX_STS),
|
|
+ (int)(ep->epsts));
|
|
+ return;
|
|
+ }
|
|
+ td_data = (struct ioh_udc_data_dma_desc *)
|
|
+ phys_to_virt(td_data->next);
|
|
+ }
|
|
+#else
|
|
+ if ((req->td_data_last->status & IOH_UDC_RXTX_STS) !=
|
|
+ IOH_UDC_RTS_SUCC) {
|
|
+ IOH_LOG(KERN_ERR, "Invalid RXTX status (0x%08x)\
|
|
+ epstatus=0x%08x\n",
|
|
+ (req->td_data_last->status &
|
|
+ IOH_UDC_RXTX_STS),
|
|
+ (int)(ep->epsts));
|
|
+ return;
|
|
+ }
|
|
+#endif
|
|
+ req->req.actual = req->req.length;
|
|
+ req->td_data_last->status = IOH_UDC_BS_HST_BSY |
|
|
+ IOH_UDC_DMA_LAST;
|
|
+ req->td_data->status = IOH_UDC_BS_HST_BSY |
|
|
+ IOH_UDC_DMA_LAST;
|
|
+ /* complete req */
|
|
+ complete_req(ep, req, 0);
|
|
+ req->dma_going = 0;
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ while (ioh_udc_read_ep_control(ep->regs) &
|
|
+ (1 << UDC_EPCTL_S))
|
|
+ udelay(100);
|
|
+
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num +
|
|
+ UDC_EPINT_OUT_EP0));
|
|
+ } else {
|
|
+ ioh_udc_disable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num +
|
|
+ UDC_EPINT_OUT_EP0));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_complete_receiver(struct ioh_udc_ep *ep)
|
|
+ *@brief This function completes a receiver
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If request queue is not empty, get next request
|
|
+ * - If buffer status is DMA Done
|
|
+ * - Disable DMA using ioh_udc_clear_dma API
|
|
+ * - Get size of Rx bytes
|
|
+ * - Set the descriptor status
|
|
+ * - Complete the request using complete_req API
|
|
+ * - If there is a new/failed requests try that
|
|
+ * now using ioh_udc_start_rxrequest API
|
|
+ * - Otherwise
|
|
+ * - Set receive ready using ioh_udc_ep_set_rrdy
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - ioh_udc_clear_dma
|
|
+ * - complete_req
|
|
+ * - ioh_udc_start_rxrequest
|
|
+ * - ioh_udc_ep_set_rrdy
|
|
+ */
|
|
+static void ioh_udc_complete_receiver(struct ioh_udc_ep *ep)
|
|
+{
|
|
+ struct ioh_udc_request *req;
|
|
+ unsigned int count;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_complete_receiver");
|
|
+
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ /* next request */
|
|
+ req = list_entry(ep->queue.next, struct ioh_udc_request, queue);
|
|
+ if (req && (req->td_data_last->status & IOH_UDC_BUFF_STS) ==
|
|
+ IOH_UDC_BS_DMA_DONE) {
|
|
+ IOH_DEBUG("ioh_udc_complete_receiver: ep%d%s DMA Done",
|
|
+ ep->num, (ep->in ? "in" : "out"));
|
|
+ /* Disable DMA */
|
|
+ ioh_udc_clear_dma(ep->dev->regs, DMA_DIR_RX);
|
|
+#ifdef DMA_PPB_WITH_DESC_UPDATE
|
|
+{
|
|
+ /* Get Rx bytes */
|
|
+ struct ioh_udc_data_dma_desc *td_data = req->td_data;
|
|
+ for (i = 0, count = 0; i < req->chain_len; i++) {
|
|
+ if ((td_data->status & IOH_UDC_RXTX_STS) !=
|
|
+ IOH_UDC_RTS_SUCC) {
|
|
+ IOH_LOG(KERN_ERR, "Invalid RXTX status\
|
|
+ (0x%08x) epstatus=0x%08x\n",
|
|
+ (td_data->status &
|
|
+ IOH_UDC_RXTX_STS),
|
|
+ (int)(ep->epsts));
|
|
+ return;
|
|
+ }
|
|
+ count += td_data->status & IOH_UDC_RXTX_BYTES;
|
|
+ td_data = (struct ioh_udc_data_dma_desc *)\
|
|
+ phys_to_virt(td_data->next);
|
|
+ }
|
|
+}
|
|
+#else
|
|
+ if ((req->td_data_last->status & IOH_UDC_RXTX_STS) !=
|
|
+ IOH_UDC_RTS_SUCC) {
|
|
+ IOH_LOG(KERN_ERR, "Invalid RXTX status (0x%08x)\
|
|
+ epstatus=0x%08x\n",
|
|
+ (req->td_data_last->status &
|
|
+ IOH_UDC_RXTX_STS),
|
|
+ (int)(ep->epsts));
|
|
+ return;
|
|
+ }
|
|
+ count = req->td_data_last->status & IOH_UDC_RXTX_BYTES;
|
|
+#endif
|
|
+ if ((count == 0) && (req->req.length ==
|
|
+ UDC_DMA_MAXPACKET)) {
|
|
+ /* on 64k packets the RXBYTES field is zero */
|
|
+ count = UDC_DMA_MAXPACKET;
|
|
+ }
|
|
+
|
|
+ /* Set the descriptor status */
|
|
+ req->td_data->status |= IOH_UDC_DMA_LAST;
|
|
+ req->td_data_last->status |= IOH_UDC_BS_HST_BSY;
|
|
+
|
|
+ req->dma_going = 0;
|
|
+ /* complete request */
|
|
+ req->req.actual = count;
|
|
+ complete_req(ep, req, 0);
|
|
+
|
|
+ /* If there is a new/failed requests try that now */
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ req = list_entry(ep->queue.next,
|
|
+ struct ioh_udc_request, queue);
|
|
+ ioh_udc_start_rxrequest(ep, req);
|
|
+ }
|
|
+ }
|
|
+#ifdef DMA_PPB_WITH_DESC_UPDATE
|
|
+ else {
|
|
+ IOH_DEBUG("ioh_udc_complete_receiver: ep%d%s \
|
|
+ DMA not Done", ep->num, (ep->in ? "in" : "out"));
|
|
+ ioh_udc_ep_set_rrdy(ep->regs);
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_start_next_txrequest(struct ioh_udc_ep *ep)
|
|
+ *@brief This function starts the next transmission requirement
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@remarks The following actions are performed:
|
|
+ *- If poll demand bit is set, return
|
|
+ *- If request queue is not empty, get next request from the queue
|
|
+ *- If the DMA going flag of next request queue is 1, invoke the following
|
|
+ * actions.
|
|
+ * - Clear the descriptor pointer
|
|
+ * - Set the all descriptor status to "Host ready"
|
|
+ * - Write the descriptor pointer
|
|
+ * - Enable the Tx DMA
|
|
+ * - Set the poll demand bit
|
|
+ * - Enable the interrupts of endpoint
|
|
+ * - Clear the status of NAK
|
|
+ *@see
|
|
+ * - ioh_udc_read_ep_control
|
|
+ * - ioh_udc_ep_set_ddptr
|
|
+ * - ioh_udc_set_dma
|
|
+ * - ioh_udc_ep_set_pd
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ */
|
|
+static void ioh_udc_start_next_txrequest(struct ioh_udc_ep *ep)
|
|
+{
|
|
+ struct ioh_udc_request *req;
|
|
+ IOH_DEBUG("ioh_udc_start_next_txrequest: enter");
|
|
+ if (ioh_udc_read_ep_control(ep->regs) & (1 << UDC_EPCTL_P))
|
|
+ return;
|
|
+
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ /* next request */
|
|
+ req = list_entry(ep->queue.next, struct ioh_udc_request, queue);
|
|
+ if (req && !req->dma_going) {
|
|
+ IOH_DEBUG("Set request: req=%p req->td_data=%p", req,
|
|
+ req->td_data);
|
|
+ if (req->td_data) {
|
|
+ struct ioh_udc_data_dma_desc *td_data;
|
|
+
|
|
+ while (ioh_udc_read_ep_control(ep->regs) &
|
|
+ (1 << UDC_EPCTL_S))
|
|
+ udelay(100);
|
|
+
|
|
+ req->dma_going = 1;
|
|
+ /* Clear the descriptor pointer */
|
|
+ ioh_udc_ep_set_ddptr(ep->regs, 0);
|
|
+
|
|
+ td_data = req->td_data;
|
|
+ while (1) {
|
|
+ td_data->status = (td_data->status &
|
|
+ ~IOH_UDC_BUFF_STS) |
|
|
+ IOH_UDC_BS_HST_RDY;
|
|
+ if ((td_data->status &
|
|
+ IOH_UDC_DMA_LAST) ==
|
|
+ IOH_UDC_DMA_LAST)
|
|
+ break;
|
|
+
|
|
+ td_data =
|
|
+ (struct ioh_udc_data_dma_desc *)\
|
|
+ phys_to_virt(td_data->next);
|
|
+ }
|
|
+ /* Write the descriptor pointer */
|
|
+ ioh_udc_ep_set_ddptr(ep->regs,
|
|
+ req->td_data_phys);
|
|
+ /* Enable the Tx DMA */
|
|
+ ioh_udc_set_dma(ep->dev->regs, DMA_DIR_TX);
|
|
+ /* Set the poll demand bit */
|
|
+ ioh_udc_ep_set_pd(ep->regs);
|
|
+ /* Enable the interrupts of endpoint */
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num :\
|
|
+ ep->num + UDC_EPINT_OUT_EP0));
|
|
+ /* Clear NAK */
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_start_rxrequest(struct ioh_udc_ep *ep,
|
|
+ * struct ioh_udc_request *req)
|
|
+ *@brief This function starts the receive requirement.
|
|
+ *@param ep Reference to the endpoint structure
|
|
+ *@param req Reference to the request structure
|
|
+ *@remarks The following actions are performed:
|
|
+ * - Disable the Rx DMA
|
|
+ * - Set the status bits for all descriptors
|
|
+ * - Write the descriptor pointer
|
|
+ * - Enable the interrupts of endpoint
|
|
+ * - Enable the Rx DMA
|
|
+ * - Clear NAK
|
|
+ * - Set the receive ready
|
|
+ *@see
|
|
+ * - ioh_udc_clear_dma
|
|
+ * - ioh_udc_ep_set_ddptr
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_set_dma
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ * - ioh_udc_ep_set_rrdy
|
|
+ */
|
|
+static void ioh_udc_start_rxrequest(struct ioh_udc_ep *ep,
|
|
+ struct ioh_udc_request *req)
|
|
+{
|
|
+ struct ioh_udc_data_dma_desc *td_data;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_start_request: enter");
|
|
+ /* Disable Rx DMA */
|
|
+ ioh_udc_clear_dma(ep->dev->regs, DMA_DIR_RX);
|
|
+ td_data = req->td_data;
|
|
+ ep->td_data = req->td_data;
|
|
+ /* Set the status bits for all descriptors */
|
|
+ while (1) {
|
|
+ td_data->status = (td_data->status & ~IOH_UDC_BUFF_STS) |
|
|
+ IOH_UDC_BS_HST_RDY;
|
|
+ if ((td_data->status & IOH_UDC_DMA_LAST) == IOH_UDC_DMA_LAST)
|
|
+ break;
|
|
+
|
|
+ td_data = (struct ioh_udc_data_dma_desc *) \
|
|
+ phys_to_virt(td_data->next);
|
|
+ }
|
|
+ /* Write the descriptor pointer */
|
|
+ ioh_udc_ep_set_ddptr(ep->regs, req->td_data_phys);
|
|
+ req->dma_going = 1;
|
|
+ /* Enable endpoint interrupts */
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs, 1 <<
|
|
+ (ep->num + UDC_EPINT_OUT_EP0));
|
|
+ /* Enable Rx DMA */
|
|
+ ioh_udc_set_dma(ep->dev->regs, DMA_DIR_RX);
|
|
+ /* Clear NAK */
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+ /* Set receive ready */
|
|
+ ioh_udc_ep_set_rrdy(ep->regs);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn static void ioh_udc_postsvc_epinters(struct ioh_udc_dev *dev,
|
|
+ * int ep_num)
|
|
+ *@brief This function enables end point interrupts and clears NAK status
|
|
+ *@param dev Reference to the device structure
|
|
+ *@param ep_num End point number
|
|
+ *@return none
|
|
+ *@remarks The following actions are performed:
|
|
+ * - If the end point request queue is not empty,
|
|
+ * - Enable the endpoint interrupts using
|
|
+ * ioh_udc_enable_ep_interrupts
|
|
+ * - Clear Endpoint NAK status using ioh_udc_ep_clear_nak
|
|
+ *@see
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ */
|
|
+static void ioh_udc_postsvc_epinters(struct ioh_udc_dev *dev, int ep_num)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ struct ioh_udc_request *req;
|
|
+ ep = &dev->ep[2*ep_num];
|
|
+
|
|
+ if (!list_empty(&ep->queue)) {
|
|
+ req = list_entry(ep->queue.next, struct ioh_udc_request, queue);
|
|
+ ioh_udc_enable_ep_interrupts(ep->dev->regs,
|
|
+ 1 << (ep->in ? ep->num : ep->num + UDC_EPINT_OUT_EP0));
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Interrupt Service Routine */
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ *@fn irqreturn_t ioh_udc_isr(int irq, void *pdev)
|
|
+ *@brief This function handles interrupts from the IOH USB Device
|
|
+ *@param irq Interrupt request number
|
|
+ *@param dev Reference to the device structure
|
|
+ *@return none
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Read the device and endpoint interrupt registers
|
|
+ *- Clear the device and endpoint interrupts using
|
|
+ * ioh_udc_write_device_interrupts
|
|
+ * and ioh_udc_write_ep_interrupts APIs respectively
|
|
+ *- If there are no interrupts, return IRQ_NONE
|
|
+ *- If there are any device interrupt(s), invoke ioh_udc_dev_isr
|
|
+ * to process those
|
|
+ *- Invoke ioh_udc_read_all_epstatus to read endpoint interrupts status
|
|
+ *- If there is interrupt asserted on EP0 IN,
|
|
+ * invoke ioh_udc_svc_control_in and ioh_udc_postsvc_epinters.
|
|
+ *- If there is interrupt asserted on EP0 OUT, invoke ioh_udc_svc_control_out
|
|
+ *- For all other IN end points for which the interrupt bit is set,
|
|
+ * invoke ioh_udc_svc_data_in and ioh_udc_postsvc_epinters.
|
|
+ *- For all other OUT end points for which the interrupt bit is set,
|
|
+ * invoke ioh_udc_svc_data_out
|
|
+ *- Return IRQ_HANDLED
|
|
+ *@see
|
|
+ * - ioh_udc_read_device_interrupts
|
|
+ * - ioh_udc_read_ep_interrupts
|
|
+ * - ioh_udc_write_device_interrupts
|
|
+ * - ioh_udc_write_ep_interrupts
|
|
+ * - ioh_udc_dev_isr
|
|
+ * - ioh_udc_svc_control_out
|
|
+ * - ioh_udc_svc_control_in
|
|
+ * - ioh_udc_svc_data_out
|
|
+ * - ioh_udc_svc_data_in
|
|
+ * - ioh_udc_read_all_epstatus
|
|
+ * - ioh_udc_postsvc_epinters
|
|
+ */
|
|
+irqreturn_t ioh_udc_isr(int irq, void *pdev)
|
|
+{
|
|
+ struct ioh_udc_dev *dev;
|
|
+ u32 dev_intr, ep_intr;
|
|
+ int i;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_isr: enter");
|
|
+ dev = (struct ioh_udc_dev *) pdev;
|
|
+ dev_intr = ioh_udc_read_device_interrupts(dev->regs);
|
|
+ ep_intr = ioh_udc_read_ep_interrupts(dev->regs);
|
|
+
|
|
+ if (dev_intr != 0) {
|
|
+ /* Clear device interrupts */
|
|
+ ioh_udc_write_device_interrupts(dev->regs, dev_intr);
|
|
+ }
|
|
+ if (ep_intr != 0) {
|
|
+ /* Clear ep interrupts */
|
|
+ ioh_udc_write_ep_interrupts(dev->regs, ep_intr);
|
|
+ }
|
|
+ if ((dev_intr == 0) && (ep_intr == 0)) {
|
|
+ IOH_DEBUG("ioh_udc_isr: exit IRQ_NONE");
|
|
+ return IRQ_NONE;
|
|
+ }
|
|
+ spin_lock(&dev->lock);
|
|
+
|
|
+ if (dev_intr != 0) {
|
|
+ IOH_DEBUG("ioh_udc_isr: device intr 0x%x", dev_intr);
|
|
+ ioh_udc_dev_isr(dev, dev_intr);
|
|
+ }
|
|
+
|
|
+ if (ep_intr != 0) {
|
|
+ IOH_DEBUG("ioh_udc_isr: ep intr 0x%x", ep_intr);
|
|
+ ioh_udc_read_all_epstatus(dev, ep_intr);
|
|
+
|
|
+ /* Process Control In interrupts, if present */
|
|
+ if (ep_intr & (1 << UDC_EPINT_IN_EP0)) {
|
|
+ ioh_udc_svc_control_in(dev);
|
|
+ ioh_udc_postsvc_epinters(dev, 0);
|
|
+ }
|
|
+ /* Process Control Out interrupts, if present */
|
|
+ if (ep_intr & (1 << UDC_EPINT_OUT_EP0))
|
|
+ ioh_udc_svc_control_out(dev);
|
|
+
|
|
+ /* Process data in end point interrupts */
|
|
+ for (i = 1; i < IOH_UDC_USED_EP_NUM; i++) {
|
|
+ if (ep_intr & (1 << i)) {
|
|
+ ioh_udc_svc_data_in(dev, i);
|
|
+ ioh_udc_postsvc_epinters(dev, i);
|
|
+ }
|
|
+ }
|
|
+ /* Process data out end point interrupts */
|
|
+ for (i = UDC_EPINT_OUT_EP1; i < (UDC_EPINT_OUT_EP0 +
|
|
+ IOH_UDC_USED_EP_NUM); i++) {
|
|
+ if (ep_intr & (1 << i))
|
|
+ ioh_udc_svc_data_out(dev, i -
|
|
+ UDC_EPINT_OUT_EP0);
|
|
+ }
|
|
+ }
|
|
+ spin_unlock(&dev->lock);
|
|
+ IOH_DEBUG("ioh_udc_isr: exit IRQ_HANDLED");
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn void ioh_udc_activate_control_ep(struct ioh_udc_dev *dev)
|
|
+ *@brief This function enables the control endpoints for traffic after a reset
|
|
+ *@param dev Reference to the device structure
|
|
+ *@return none
|
|
+ *@remarks The following actions are performed:
|
|
+ *- Setup IN endpoint,
|
|
+ * - Flush the TX FIFO using ioh_udc_ep_fifo_flush API
|
|
+ * - Set the buffer size for TXFIFO entries of EP0_IN using
|
|
+ * ioh_udc_ep_set_bufsz API
|
|
+ * - Set max packet size of EP0 IN using ioh_udc_ep_set_maxpkt API
|
|
+ * - Initialize the IN EP Descriptor
|
|
+ *- Setup OUT endpoint,
|
|
+ * - Flush the RX FIFO using ioh_udc_ep_fifo_flush API
|
|
+ * - Set max packet size of EP0 OUT using ioh_udc_ep_set_maxpkt.
|
|
+ * - Set max packet size of EP0 OUT UDC CSR
|
|
+ * - Initialize the SETUP buffer
|
|
+ * - Write the DMA Descriptor address
|
|
+ * - Write the Setup Descriptor address
|
|
+ * - Initialize the DMA descriptor
|
|
+ * - Clear NAK
|
|
+ *@see
|
|
+ * - ioh_udc_clear_ep_control
|
|
+ * - ioh_udc_ep_fifo_flush
|
|
+ * - ioh_udc_ep_set_bufsz
|
|
+ * - ioh_udc_ep_set_maxpkt
|
|
+ * - ioh_udc_write_csr
|
|
+ * - ioh_udc_init_setup_buff
|
|
+ * - ioh_udc_ep_set_subptr
|
|
+ * - ioh_udc_ep_set_ddptr
|
|
+ * - ioh_udc_ep_clear_nak
|
|
+ */
|
|
+void ioh_udc_activate_control_ep(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ u32 val;
|
|
+ /* Setup IN endpoint ----------------------------- */
|
|
+ ep = &dev->ep[UDC_EP0IN_IDX];
|
|
+
|
|
+ /* Flush TX fifo */
|
|
+ ioh_udc_clear_ep_control(ep->regs);
|
|
+ ioh_udc_ep_fifo_flush(ep->regs, ep->in);
|
|
+
|
|
+ /* Set buffer size (tx fifo entries) of EP0_IN */
|
|
+ ioh_udc_ep_set_bufsz(ep->regs, UDC_EP0IN_BUFF_SIZE, ep->in);
|
|
+
|
|
+ /* Set max packet size of EP0_IN */
|
|
+ ioh_udc_ep_set_maxpkt(ep->regs, UDC_EP0IN_MAX_PKT_SIZE);
|
|
+
|
|
+ /* Initialize the IN EP Descriptor */
|
|
+ ep->td_data = NULL;
|
|
+ ep->td_stp = NULL;
|
|
+ ep->td_data_phys = 0;
|
|
+ ep->td_stp_phys = 0;
|
|
+
|
|
+ /* Setup OUT endpoint ----------------------------- */
|
|
+ ep = &dev->ep[UDC_EP0OUT_IDX];
|
|
+
|
|
+ /* Flush RX fifo */
|
|
+ ioh_udc_clear_ep_control(ep->regs);
|
|
+ ioh_udc_ep_fifo_flush(ep->regs, ep->in);
|
|
+
|
|
+ /* Set buffer size (rx fifo entries) of EP0_OUT */
|
|
+ ioh_udc_ep_set_bufsz(ep->regs, UDC_EP0OUT_BUFF_SIZE, ep->in);
|
|
+
|
|
+ /* Set max packet size of EP0_OUT */
|
|
+ ioh_udc_ep_set_maxpkt(ep->regs, UDC_EP0OUT_MAX_PKT_SIZE);
|
|
+
|
|
+ /* Set max packet size of EP0 OUT UDC CSR */
|
|
+ val = UDC_EP0OUT_MAX_PKT_SIZE << UDC_CSR_NE_MAX_PKT_OFS;
|
|
+ ioh_udc_write_csr(val, (u32) (&dev->csr->ne[UDC_EP0OUT_IDX]));
|
|
+
|
|
+ /* Initialize the SETUP buffer */
|
|
+ ioh_udc_init_setup_buff(ep->td_stp);
|
|
+
|
|
+ /* Write dma desc address */
|
|
+ ioh_udc_ep_set_subptr(ep->regs, ep->td_stp_phys);
|
|
+
|
|
+ /* Write Setup desc address */
|
|
+ ioh_udc_ep_set_ddptr(ep->regs, ep->td_data_phys);
|
|
+
|
|
+ /* Initialize dma descriptor */
|
|
+ ep->td_data->status = IOH_UDC_DMA_LAST;
|
|
+ ep->td_data->dataptr = dma_addr;
|
|
+ ep->td_data->next = ep->td_data_phys;
|
|
+
|
|
+ /* Clear NAK */
|
|
+ ioh_udc_ep_clear_nak(ep->regs);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn ioh_udc_read_all_epstatus(struct ioh_udc_dev *dev, u32 ep_intr)
|
|
+ *@brief This function read all endpoint status
|
|
+ *@param dev Reference to the device structure
|
|
+ *@param ep_intr Status of endpoint interrupt
|
|
+ *@return none
|
|
+ *@remarks The following actions are performed:
|
|
+ * - For each of the used endpoints,
|
|
+ * - Read the endpoint status using
|
|
+ * ioh_udc_read_ep_status
|
|
+ * - Invoke ioh_udc_clear_ep_status to clear
|
|
+ * the status
|
|
+ *@see
|
|
+ * - ioh_udc_read_ep_status
|
|
+ * - ioh_udc_clear_ep_status
|
|
+ */
|
|
+static void ioh_udc_read_all_epstatus(struct ioh_udc_dev *dev, u32 ep_intr)
|
|
+{
|
|
+ int i;
|
|
+ struct ioh_udc_ep *ep;
|
|
+
|
|
+ for (i = 0; i < IOH_UDC_USED_EP_NUM; i++) {
|
|
+ /* IN */
|
|
+ if (ep_intr & (0x1 << i)) {
|
|
+ ep = &dev->ep[2*i];
|
|
+ ep->epsts = ioh_udc_read_ep_status(ep->regs);
|
|
+ ioh_udc_clear_ep_status(ep->regs, ep->epsts);
|
|
+ }
|
|
+ /* OUT */
|
|
+ if (ep_intr & (0x10000 << i)) {
|
|
+ ep = &dev->ep[2*i+1];
|
|
+ ep->epsts = ioh_udc_read_ep_status(ep->regs);
|
|
+ ioh_udc_clear_ep_status(ep->regs, ep->epsts);
|
|
+ }
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn static void ioh_udc_setup_ep0(struct ioh_udc_dev *dev)
|
|
+ *@brief This function enables control endpoint for traffic
|
|
+ *@remarks The following actions are performed:
|
|
+ * - Enable ep0 interrupts using
|
|
+ * ioh_udc_enable_ep_interrupts API.
|
|
+ * - Enable device interrupts (Set configuration command
|
|
+ * interrupt,
|
|
+ * Set interface command interrupt, ENUM interrupt,
|
|
+ * USB Reset interrupt)
|
|
+ * using ioh_udc_enable_interrupts API
|
|
+ *@param dev Reference to the device structure
|
|
+ *@return none
|
|
+ *@see
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_enable_interrupts
|
|
+ */
|
|
+static void ioh_udc_setup_ep0(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ /* enable ep0 interrupts */
|
|
+ ioh_udc_enable_ep_interrupts(dev->regs, 1 << UDC_EPINT_IN_EP0 |
|
|
+ 1 << UDC_EPINT_OUT_EP0);
|
|
+
|
|
+ /* enable device interrupts */
|
|
+ ioh_udc_enable_interrupts(dev->regs, (1 << UDC_DEVINT_UR) |
|
|
+ (1 << UDC_DEVINT_US) | (1 << UDC_DEVINT_ES) |
|
|
+ (1 << UDC_DEVINT_ENUM) | (1 << UDC_DEVINT_SI) |
|
|
+ (1 << UDC_DEVINT_SC));
|
|
+ IOH_DEBUG("Dev intr mask set to %x",
|
|
+ IOH_READ32((u32 *)&(dev->regs->devirqmsk)));
|
|
+ IOH_DEBUG("Ep intr mask set to %x",
|
|
+ IOH_READ32((u32 *)&(dev->regs->epirqmsk)));
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ *@fn ioh_udc_init_setup_buff(struct ioh_udc_stp_dma_desc *td_stp)
|
|
+ *@brief This function initializes the SETUP buffer
|
|
+ *@param td_stp Reference to the SETP buffer structure
|
|
+ *@return none
|
|
+ */
|
|
+static void ioh_udc_init_setup_buff(struct ioh_udc_stp_dma_desc *td_stp)
|
|
+{
|
|
+ static u32 pky_marker;
|
|
+ IOH_DEBUG("ioh_udc_init_setup_buff");
|
|
+
|
|
+ if (td_stp == NULL) {
|
|
+ IOH_DEBUG("SETUP BUFF == NULL");
|
|
+ return;
|
|
+ }
|
|
+ td_stp->reserved = ++pky_marker;
|
|
+ td_stp->data12 = 0xFFFFFFFF;
|
|
+ td_stp->data34 = 0xFFFFFFFF;
|
|
+ td_stp->status = IOH_UDC_BS_HST_RDY;
|
|
+}
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc.h
|
|
@@ -0,0 +1,172 @@
|
|
+ /*!
|
|
+ * @file ioh_udc.h
|
|
+ * @brief
|
|
+ * The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ * It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or
|
|
+ * interrupt type).
|
|
+ *
|
|
+ * The IOH USB device controller driver provides required interface
|
|
+ * to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ * @version 0.96
|
|
+ *
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * History:
|
|
+ * Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ * modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _IOH_UDC_H_
|
|
+#include <linux/types.h>
|
|
+#include <linux/irq.h>
|
|
+
|
|
+extern const struct usb_gadget_ops ioh_udc_ops;
|
|
+extern struct ioh_udc_dev *ioh_udc;
|
|
+
|
|
+extern irqreturn_t ioh_udc_isr(int irq, void *pdev);
|
|
+extern int ioh_udc_pcd_init(struct ioh_udc_dev *dev);
|
|
+extern void ioh_udc_dev_isr(struct ioh_udc_dev *dev, u32 dev_intr);
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+ * @struct ioh_udc_request
|
|
+ * @brief Structure holding a IOH USB device request
|
|
+ */
|
|
+struct ioh_udc_request /* request packet */
|
|
+{
|
|
+ struct usb_request req; /**< embedded ep request */
|
|
+ dma_addr_t td_data_phys; /**< phys. address */
|
|
+ struct ioh_udc_data_dma_desc *td_data;/*< first dma desc. of chain */
|
|
+ struct ioh_udc_data_dma_desc *td_data_last;/**< last dma desc.
|
|
+ of chain */
|
|
+ struct list_head queue; /**< associated queue */
|
|
+
|
|
+ /* flags */
|
|
+ unsigned dma_going:1,/**< DMA in progress for request */
|
|
+ dma_mapped:1,/**< DMA memory mapped
|
|
+ for request */
|
|
+ dma_done:1; /**< DMA completed
|
|
+ for request */
|
|
+ unsigned chain_len; /**< chain length */
|
|
+};
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+ * @struct ioh_udc_ep
|
|
+ * @brief Structure holding a IOH USB device Endpoint information
|
|
+ */
|
|
+struct ioh_udc_ep {
|
|
+ struct usb_ep ep; /**< embedded ep request */
|
|
+ dma_addr_t td_stp_phys;/**< for setup request */
|
|
+ dma_addr_t td_data_phys;/**< for data request */
|
|
+ struct ioh_udc_stp_dma_desc *td_stp; /**< for setup request */
|
|
+ struct ioh_udc_data_dma_desc *td_data; /**< for data request */
|
|
+
|
|
+ struct ioh_udc_dev *dev; /**< reference to device struct */
|
|
+ struct ioh_udc_ep_regs __iomem *regs;
|
|
+ u32 __iomem *dma; /**< dma enabled or not */
|
|
+ const struct usb_endpoint_descriptor *desc; /**< for this ep */
|
|
+
|
|
+ /* queue for requests */
|
|
+ struct list_head queue;
|
|
+
|
|
+ unsigned num:5; /**< endpoint number */
|
|
+ unsigned in:1; /**< endpoint is IN */
|
|
+ unsigned halted; /**< endpoint halted? */
|
|
+ unsigned long epsts; /**< Endpoint status */
|
|
+};
|
|
+
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+ * @struct ioh_udc_dev
|
|
+ * @brief Structure holding complete information of the
|
|
+ IOH USB device
|
|
+ */
|
|
+struct ioh_udc_dev {
|
|
+ struct usb_gadget gadget; /**< gadget driver data */
|
|
+ struct usb_gadget_driver *driver;/**< reference to gadget
|
|
+ driver bound */
|
|
+ struct pci_dev *pdev; /**< reference to the
|
|
+ PCI device */
|
|
+
|
|
+ /* all endpoints */
|
|
+ struct ioh_udc_ep ep[IOH_UDC_EP_NUM]; /**< array of
|
|
+ endpoints */
|
|
+ spinlock_t lock; /* protects all state */
|
|
+ /* operational flags */
|
|
+ unsigned active:1, /**< enabled the PCI device */
|
|
+ stall:1, /**< stall requested */
|
|
+ prot_stall:1, /**< protcol stall requested */
|
|
+ irq_registered:1, /**< irq registered with system */
|
|
+ mem_region:1, /**< device memory mapped */
|
|
+ registered:1, /**< driver regsitered with system */
|
|
+ suspended:1, /**< driver in suspended state */
|
|
+ connected:1, /**< gadget driver associated */
|
|
+ set_cfg_not_acked:1,/**< pending acknowledgement
|
|
+ 4 setup */
|
|
+ waiting_zlp_ack:1;/**< pending acknowledgement 4 ZLP */
|
|
+
|
|
+ /* registers */
|
|
+ struct ioh_udc_csrs __iomem *csr; /**< address of config & status
|
|
+ registers */
|
|
+ struct ioh_udc_regs __iomem *regs; /**< address of device
|
|
+ registers */
|
|
+ struct ioh_udc_ep_regs __iomem *ep_regs; /**< address of endpoint
|
|
+ registers */
|
|
+
|
|
+ /* DMA desc pools */
|
|
+ struct pci_pool *data_requests; /**< DMA pool for
|
|
+ data requests */
|
|
+ struct pci_pool *stp_requests; /**< DMA pool for
|
|
+ setup requests */
|
|
+
|
|
+ /* device data */
|
|
+ unsigned long phys_addr; /**< of device memory */
|
|
+ void __iomem *virt_addr;/**< for mapped device
|
|
+ memory */
|
|
+ unsigned irq; /**< IRQ line for the device */
|
|
+
|
|
+ struct ioh_udc_cfg_data cfg_data;/**< current cfg, intf,
|
|
+ and alt in use */
|
|
+};
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+ * @struct ioh_udc_ep
|
|
+ * @brief Structure holding setup request data
|
|
+ */
|
|
+union ioh_udc_setup_data {
|
|
+ u32 data[2]; /**< 8 bytes of setup data */
|
|
+ struct usb_ctrlrequest request; /**< setup request for gadget driver */
|
|
+};
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ * @fn ioh_udc_activate_control_ep(struct ioh_udc_dev *dev)
|
|
+ * @brief Performs necessary operations to enable endpoint 0
|
|
+ */
|
|
+extern void ioh_udc_activate_control_ep(struct ioh_udc_dev *dev);
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ * @fn empty_req_queue(struct ioh_udc_ep *ep)
|
|
+ * @brief Removes all requests queued on the endpoint
|
|
+ */
|
|
+extern void empty_req_queue(struct ioh_udc_ep *ep);
|
|
+
|
|
+#endif /* _IOH_UDC_H_ */
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc_hal.c
|
|
@@ -0,0 +1,1110 @@
|
|
+ /*!
|
|
+ * @file ioh_udc_hal.c
|
|
+ * @brief
|
|
+ * The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ * It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or
|
|
+ * interrupt type).
|
|
+ *
|
|
+ * The IOH USB device controller driver provides required interface
|
|
+ * to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ * @version 0.96
|
|
+ *
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * History:
|
|
+ * Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ * modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+/*#include <asm/io.h>*/
|
|
+#include <linux/io.h>
|
|
+#include "pch_common.h"
|
|
+#include "pch_debug.h"
|
|
+
|
|
+#include <linux/usb/ch9.h>
|
|
+#include "pch_udc_hal.h"
|
|
+
|
|
+#define INLINE inline
|
|
+static u32 ioh_udc_base;
|
|
+
|
|
+#define MAX_LOOP 200
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn inline void ioh_udc_write_csr(unsigned long val, unsigned long
|
|
+ * addr)
|
|
+ * @remarks The following actions are performed:
|
|
+ * - Wait for any ongoing access to config registers to
|
|
+ * complete, by polling the CSR busy flag.
|
|
+ * - Write the value to the CSR register.
|
|
+ * - Wait till the write gets completed by polling the
|
|
+ * CSR busy flag.
|
|
+ * @param val [IN] value to be written to CSR register
|
|
+ * @param addr [IN] address of CSR register
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_write_csr(unsigned long val, unsigned long addr)
|
|
+{
|
|
+ int count = MAX_LOOP;
|
|
+
|
|
+ /* Wait till idle */
|
|
+ while ((count > 0) &&\
|
|
+ (IOH_READ32((unsigned long *)(IOH_UDC_CSR_BUSY_ADDR + ioh_udc_base)) &
|
|
+ IOH_UDC_CSR_BUSY))
|
|
+ count--;
|
|
+
|
|
+ if (count < 0)
|
|
+ IOH_DEBUG("ioh_udc_write_csr: wait error; count = %x", count);
|
|
+
|
|
+ iowrite32(val, (unsigned long *)addr);
|
|
+ /* Wait till idle */
|
|
+ count = MAX_LOOP;
|
|
+ while ((count > 0) &&
|
|
+ (IOH_READ32((unsigned long *)(IOH_UDC_CSR_BUSY_ADDR + ioh_udc_base)) &
|
|
+ IOH_UDC_CSR_BUSY))
|
|
+ count--;
|
|
+
|
|
+ if (count < 0)
|
|
+ IOH_DEBUG("ioh_udc_write_csr: wait error; count = %x", count);
|
|
+
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_read_csr(unsigned long addr)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - This function waits for any access to internal
|
|
+ * USB_DEVICE registers to
|
|
+ * complete and then reads the config register
|
|
+ * @param addr [IN] address of CSR register
|
|
+ * @retval u32 - content of CSR register
|
|
+ */
|
|
+INLINE u32 ioh_udc_read_csr(unsigned long addr)
|
|
+{
|
|
+ int count = MAX_LOOP;
|
|
+
|
|
+ /* Wait till idle */
|
|
+ while ((count > 0) &&
|
|
+ (IOH_READ32((unsigned long *)(IOH_UDC_CSR_BUSY_ADDR + ioh_udc_base)) &
|
|
+ IOH_UDC_CSR_BUSY))
|
|
+ count--;
|
|
+
|
|
+ if (count < 0)
|
|
+ IOH_DEBUG("ioh_udc_read_csr: wait error; count = %x", count);
|
|
+ /* Dummy read */
|
|
+ IOH_READ32((unsigned long *)addr);
|
|
+ count = MAX_LOOP;
|
|
+ /* Wait till idle */
|
|
+ while ((count > 0) &&
|
|
+ (IOH_READ32((unsigned long *)(IOH_UDC_CSR_BUSY_ADDR + ioh_udc_base)) &
|
|
+ IOH_UDC_CSR_BUSY))
|
|
+ count--;
|
|
+ /* actual read */
|
|
+ if (count < 0)
|
|
+ IOH_DEBUG("ioh_udc_read_csr: wait error; count = %x", count);
|
|
+
|
|
+ return IOH_READ32((unsigned long *)addr);
|
|
+}
|
|
+
|
|
+/* Prints UDC device registers and endpoint irq registers */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_print_regs(u32 base)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Read UDC device registers and endpoint registers and print them
|
|
+ * @param base [IN] the remapped base address
|
|
+ */
|
|
+void ioh_udc_print_regs(u32 base)
|
|
+{
|
|
+ struct ioh_udc_data_dma_desc *data_desc;
|
|
+ struct ioh_udc_stp_dma_desc *stp_desc;
|
|
+ u32 *regs = (u32 *) (base + UDC_DEVCFG_ADDR);
|
|
+ u32 ddp, stp;
|
|
+ int i;
|
|
+
|
|
+ IOH_DEBUG("print_regs: base %x", base);
|
|
+ IOH_DEBUG("------- Device registers ---------");
|
|
+ IOH_DEBUG("dev config = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->devcfg));
|
|
+ IOH_DEBUG("dev control = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->devctl));
|
|
+ IOH_DEBUG("dev status = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->devsts));
|
|
+ IOH_DEBUG("dev int's = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->devirqsts));
|
|
+ IOH_DEBUG("dev intmask = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->devirqmsk));
|
|
+ IOH_DEBUG("dev ep int's = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->epirqsts));
|
|
+ IOH_DEBUG("dev ep intmask = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_regs *)
|
|
+ regs)->epirqmsk));
|
|
+
|
|
+ regs = (u32 *) (base + UDC_EPIN_REGS_ADDR);
|
|
+ IOH_DEBUG("------- Endpoint registers -------");
|
|
+ for (i = 0; i < IOH_UDC_USED_EP_NUM; i++) {
|
|
+ IOH_DEBUG("Endpoint IN %1d ----------", i);
|
|
+ IOH_DEBUG("Ep control = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->epctl));
|
|
+ IOH_DEBUG("Ep status = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->epsts));
|
|
+ IOH_DEBUG("Buffer size = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->bufin_framenum));
|
|
+ IOH_DEBUG("Max packet = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->bufout_maxpkt));
|
|
+ ddp = IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->desptr);
|
|
+ IOH_DEBUG("Data Desc Ptr = %08x", ddp);
|
|
+
|
|
+ if ((ddp != 0) && (ddp != 0xFFFFFFFF)) {
|
|
+ data_desc = phys_to_virt(ddp);
|
|
+ IOH_DEBUG(" |- status = %08x", data_desc->status);
|
|
+ IOH_DEBUG(" |- reserved = %08x", data_desc->reserved);
|
|
+ IOH_DEBUG(" |- dataptr = %08x", data_desc->dataptr);
|
|
+ IOH_DEBUG(" +- next = %08x", data_desc->next);
|
|
+ }
|
|
+ regs += UDC_EP_REG_OFS / sizeof regs;
|
|
+ }
|
|
+ regs = (u32 *) (base + UDC_EPOUT_REGS_ADDR);
|
|
+ for (i = 0; i < IOH_UDC_USED_EP_NUM; i++) {
|
|
+ IOH_DEBUG("Endpoint OUT %1d ---------", i);
|
|
+ IOH_DEBUG("Ep control = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->epctl));
|
|
+ IOH_DEBUG("Ep status = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->epsts));
|
|
+ IOH_DEBUG("Frame Number = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->bufin_framenum));
|
|
+ IOH_DEBUG("Buf / Max pkt = %08x",
|
|
+ IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->bufout_maxpkt));
|
|
+ stp = IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->subptr);
|
|
+ IOH_DEBUG("Setup buf ptr = %08x", stp);
|
|
+
|
|
+ if ((stp != 0) && (stp != 0xFFFFFFFF)) {
|
|
+ stp_desc = phys_to_virt(stp);
|
|
+ IOH_DEBUG(" |- status = %08x", stp_desc->status);
|
|
+ IOH_DEBUG(" |- reserved = %08x", stp_desc->reserved);
|
|
+ IOH_DEBUG(" |- data12 = %08x", stp_desc->data12);
|
|
+ IOH_DEBUG(" +- data34 = %08x", stp_desc->data34);
|
|
+ }
|
|
+
|
|
+ ddp = IOH_READ32((u32)&((struct ioh_udc_ep_regs *)
|
|
+ regs)->desptr);
|
|
+ IOH_DEBUG("Data Desc Ptr = %08x", ddp);
|
|
+ if ((ddp != 0) && (ddp != 0xFFFFFFFF)) {
|
|
+ data_desc = phys_to_virt(ddp);
|
|
+ IOH_DEBUG(" |- status = %08x", data_desc->status);
|
|
+ IOH_DEBUG(" |- reserved = %08x", data_desc->reserved);
|
|
+ IOH_DEBUG(" |- dataptr = %08x", data_desc->dataptr);
|
|
+ IOH_DEBUG(" +- next = %08x", data_desc->next);
|
|
+ }
|
|
+ regs += UDC_EP_REG_OFS / sizeof regs;
|
|
+ }
|
|
+ regs = (u32 *) (base + UDC_CSR_ADDR);
|
|
+ IOH_DEBUG("------- Endpoint Configuration ---");
|
|
+ for (i = 0; i < IOH_UDC_USED_EP_NUM * 2; i++) {
|
|
+ IOH_DEBUG("EP config%d = %08x", i,
|
|
+ ioh_udc_read_csr((u32)&((struct ioh_udc_csrs *)
|
|
+ regs)->ne[i]));
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_init(struct ioh_udc_regs *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - If argument passed is NULL, return.
|
|
+ * - Set the base address for USB device registers
|
|
+ * - Invoke soft reset and PHY reset
|
|
+ * - Disable and clear the Device and endpoint interrupts
|
|
+ * - Enable dynamic CSR programming, set self powered and configure device
|
|
+ * speed in the Device Config register
|
|
+ * - Enable TX DMA, BURST and THRESHOLD modes
|
|
+ * and set BURST and THRESHOLD length.
|
|
+ * - Put the device in soft disconnect mode
|
|
+ * @param dev [IN] reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_init(struct ioh_udc_regs *dev)
|
|
+{
|
|
+ u32 reset_reg;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_init: enter");
|
|
+ if (NULL == dev) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_init: Invalid address");
|
|
+ return;
|
|
+ }
|
|
+ /* Set the UDC_Global variable */
|
|
+ ioh_udc_base = (u32) dev - UDC_DEVCFG_ADDR;
|
|
+ /* Soft Reset and Reset PHY */
|
|
+ reset_reg = (ioh_udc_base + IOH_UDC_SRST_ADDR);
|
|
+ IOH_WRITE32((1 << IOH_UDC_SRST), (u32 *)(reset_reg));
|
|
+ IOH_WRITE32((1 << IOH_UDC_SRST) | (1 << IOH_UDC_PSRST),
|
|
+ (u32 *)(reset_reg));
|
|
+ mdelay(1);
|
|
+ IOH_WRITE32((1 << IOH_UDC_SRST), (u32 *)(reset_reg));
|
|
+ IOH_WRITE32(0x00, (u32 *)(reset_reg));
|
|
+ mdelay(1);
|
|
+
|
|
+ /* mask and clear all device interrupts */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devirqmsk, UDC_DEVINT_MSK);
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devirqsts, UDC_DEVINT_MSK);
|
|
+
|
|
+ /* mask and clear all ep interrupts */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->epirqmsk, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->epirqsts, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+
|
|
+ /* enable dynamic CSR programmingi, self powered and device speed */
|
|
+ if (speed_fs) {
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devcfg, (1 << UDC_DEVCFG_CSR_PRG) |
|
|
+ (1 << UDC_DEVCFG_SP) | /* set self powered */
|
|
+ UDC_DEVCFG_SPD_FS); /* program speed - full speed */
|
|
+ } else { /* defaul high speed */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devcfg, (1 << UDC_DEVCFG_CSR_PRG) |
|
|
+ (1 << UDC_DEVCFG_SP) | /* set self powered */
|
|
+ UDC_DEVCFG_SPD_HS); /* program speed - high speed */
|
|
+ }
|
|
+#ifdef DMA_PPB_WITH_DESC_UPDATE
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl,
|
|
+ (IOH_UDC_THLEN << UDC_DEVCTL_THLEN_OFS) |
|
|
+ (IOH_UDC_BRLEN << UDC_DEVCTL_BRLEN_OFS) |
|
|
+ (1 << UDC_DEVCTL_MODE) | (1 << UDC_DEVCTL_BREN) |
|
|
+ (1 << UDC_DEVCTL_DU) |
|
|
+ (1 << UDC_DEVCTL_THE));
|
|
+#else
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl,
|
|
+ (IOH_UDC_THLEN << UDC_DEVCTL_THLEN_OFS) |
|
|
+ (IOH_UDC_BRLEN << UDC_DEVCTL_BRLEN_OFS) |
|
|
+ (1 << UDC_DEVCTL_MODE) | (1 << UDC_DEVCTL_BREN) |
|
|
+ (1 << UDC_DEVCTL_THE));
|
|
+#endif
|
|
+ IOH_DEBUG("ioh_udc_init: exit");
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_exit(struct ioh_udc_regs *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Put the device in soft disconnect mode
|
|
+ * - Clear the device and endpoint interrupts
|
|
+ * and disable them
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_exit(struct ioh_udc_regs *dev)
|
|
+{
|
|
+ IOH_DEBUG("ioh_udc_exit: enter");
|
|
+ /* mask all device interrupts */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devirqmsk, UDC_DEVINT_MSK);
|
|
+
|
|
+ /* mask all ep interrupts */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->epirqmsk, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+
|
|
+ /* put device in disconnected state */
|
|
+ ioh_udc_set_disconnect(dev);
|
|
+ IOH_DEBUG("ioh_udc_exit: exit");
|
|
+}
|
|
+
|
|
+/* Initiates a remote wakeup */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_rmt_wakeup(struct ioh_udc_regs *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Set the RES (bit 1) of the device control register
|
|
+ * - Wait for 1 msec
|
|
+ * - Clear the RES bit
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_rmt_wakeup(struct ioh_udc_regs *dev)
|
|
+{
|
|
+ IOH_DEBUG("ioh_udc_rmt_wakeup: enter");
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_RES);
|
|
+ mdelay(1);
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_RES);
|
|
+ IOH_DEBUG("ioh_udc_rmt_wakeup: exit");
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_get_frame(struct ioh_udc_regs *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Retrieve the current frame from device status register
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval int
|
|
+ * - current frame
|
|
+ */
|
|
+int ioh_udc_get_frame(struct ioh_udc_regs *dev)
|
|
+{
|
|
+ u32 frame;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_get_frame: enter");
|
|
+ frame = IOH_READ32((u32)&dev->devsts);
|
|
+ IOH_DEBUG("ioh_udc_get_frame: exit");
|
|
+ return (frame & UDC_DEVSTS_TS_MASK) >> UDC_DEVSTS_TS_OFS;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_clear_selfpowered (struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Clear the 'SP' bit (bit 3) of the device config register
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_clear_selfpowered(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devcfg, 1 << UDC_DEVCFG_SP);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_set_selfpowered (struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Set the 'SP' bit (bit 3) of the device config register
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_set_selfpowered(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devcfg, 1 << UDC_DEVCFG_SP);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_set_disconnect (struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Set the SD bit (bit 10) of device control register
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_set_disconnect(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_SD);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_get_speed (struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Return the speed from Device status register ENUMSPD (bit 13, 14)
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval int
|
|
+ * - The speed(LOW=1, FULL=2, HIGH=3)
|
|
+ */
|
|
+int ioh_udc_get_speed(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = IOH_READ32((u32)&dev->devsts);
|
|
+ return (val & UDC_DEVSTS_ENUM_SPEED_MASK) >> UDC_DEVSTS_ENUM_SPEED_OFS;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_clear_disconnect (struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks The main tasks performed by this method are
|
|
+ * - Clear the SD bit (bit 10) and set the RES bit (bit 1)
|
|
+ * of the device control register
|
|
+ * - After 1msec, clear the RES bit (bit 1) of the device
|
|
+ * control register
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_clear_disconnect(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ /* Clear the disconnect */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_RES);
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_SD);
|
|
+ mdelay(1);
|
|
+ /* Resume USB signalling */
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_RES);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_vbus_session (struct ioh_udc_regs __iomem *dev, int is_active)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - If 'is_active' invoke ioh_udc_clear_disconnect to make
|
|
+ * this device available to host
|
|
+ * - Otherwise invoke ioh_udc_set_disconnect to make the
|
|
+ * device unavailable
|
|
+ * @param dev [IN] Reference to ioh_udc_regs structure
|
|
+ * @param is_active [IN] Parameter specifying the action
|
|
+ * - is_active = 0 indicating VBUS power is ending
|
|
+ * - is_active != 0 indicating VBUS power is starting
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_vbus_session(struct ioh_udc_regs __iomem *dev, int is_active)
|
|
+{
|
|
+ if (is_active == 0)
|
|
+ ioh_udc_set_disconnect(dev);
|
|
+ else
|
|
+ ioh_udc_clear_disconnect(dev);
|
|
+
|
|
+}
|
|
+
|
|
+/* Stall or clear stall of endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_stall(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - If this is a IN endpoint, flush the TX FIFO ( F bit)
|
|
+ * of the endpoint control register
|
|
+ * - Set the bit 0 (S bit) of the endpoint control register
|
|
+ * @param ep [IN] reference to structure of type
|
|
+ * ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_set_stall(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ if (EP_IS_IN(ep)) { /* flush fifo */
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_F);
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_S);
|
|
+ } else {
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_S);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Halt or clear halt of endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_clear_stall(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Clear the bit 0 (S bit) of the endpoint control register
|
|
+ * - Set the bit 8 (CNAK bit) of the endpoint control register
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_clear_stall(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ /* Clear the stall */
|
|
+ IOH_CLR_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_S);
|
|
+
|
|
+ /* clear NAK by writing CNAK */
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_CNAK);
|
|
+}
|
|
+
|
|
+
|
|
+/* Set the transfer type of endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_trfr_type(struct ioh_udc_ep_regs __iomem *ep, u8 type)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Set the transfer type of endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @param type [IN] type of endpoint
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_set_trfr_type(struct ioh_udc_ep_regs __iomem *ep, u8 type)
|
|
+{
|
|
+ IOH_WRITE32(((type << UDC_EPCTL_ET_OFS) & UDC_EPCTL_ET_MASK) ,
|
|
+ (u32)&ep->epctl);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_bufsz(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ * u32 buf_size)
|
|
+ * @remarks The main task performed by this method is:
|
|
+ * - Set the maximum packet size for the endpoint
|
|
+ * @param ep [IN] reference to structure of
|
|
+ * type ioh_udc_ep_regs
|
|
+ * @param buf_size [IN] the buffer size
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_set_bufsz(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 buf_size, u32 ep_in)
|
|
+{
|
|
+ u32 data;
|
|
+ if (ep_in) {
|
|
+ data = IOH_READ32((u32)&ep->bufin_framenum);
|
|
+ data = (data & 0xffff0000) | (buf_size & 0xffff);
|
|
+ IOH_WRITE32(data, (u32)&ep->bufin_framenum);
|
|
+ } else {
|
|
+ data = IOH_READ32((u32)&ep->bufout_maxpkt);
|
|
+ data = (buf_size << 16) | (data & 0xffff);
|
|
+ IOH_WRITE32(data, (u32)&ep->bufout_maxpkt);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_maxpkt(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ * u32 pkt_size)
|
|
+ * @remarks Set the Max packet size for the endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @param pkt_size [IN] the packet size
|
|
+ * @retval none
|
|
+ */
|
|
+/* Set the Max packet size for the endpoint */
|
|
+void ioh_udc_ep_set_maxpkt(struct ioh_udc_ep_regs __iomem *ep, u32 pkt_size)
|
|
+{
|
|
+ u32 data;
|
|
+ data = IOH_READ32((u32)&ep->bufout_maxpkt);
|
|
+ data = (data & 0xffff0000) | (pkt_size & 0xffff);
|
|
+ IOH_WRITE32(data, (u32)&ep->bufout_maxpkt);
|
|
+}
|
|
+
|
|
+/* Set the Setup buffer pointer for the endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_subptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr)
|
|
+ * @remarks Set the Setup buffer pointer for the endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @param addr [IN] address of the register
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_ep_set_subptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr)
|
|
+{
|
|
+ IOH_WRITE32(addr, (u32)&ep->subptr);
|
|
+}
|
|
+
|
|
+/* Set the Data descriptor pointer for the endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_ddptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr)
|
|
+ * @remarks Set the Data descriptor pointer for the endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @param addr [IN] address of the register
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_ep_set_ddptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr)
|
|
+{
|
|
+ IOH_WRITE32(addr, (u32)&ep->desptr);
|
|
+}
|
|
+
|
|
+/* Set the poll demand bit for the endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_pd(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Set the poll demand bit for the endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_ep_set_pd(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_P);
|
|
+}
|
|
+
|
|
+/* Set the receive ready bit for the endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_rrdy(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Set the receive ready bit for the endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_ep_set_rrdy(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ IOH_DEBUG("ioh_udc_ep_set_rrdy: ep%d%s", EP_NUM(ep),
|
|
+ (EP_IS_IN(ep) ? "in" : "out"));
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_RRDY);
|
|
+}
|
|
+
|
|
+/* Clear the receive ready bit for the endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_clear_rrdy(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Clear the receive ready bit for the endpoint
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_ep_clear_rrdy(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ IOH_CLR_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_RRDY);
|
|
+}
|
|
+
|
|
+/* Enabling RX/TX DMA */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_set_dma(struct ioh_udc_regs __iomem *dev, int dir)
|
|
+ * @remarks Set the 'TDE' or RDE bit of device control register depending
|
|
+ * on the direction specified
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param dir [IN] whether Tx or Rx
|
|
+ * - dir = DMA_DIR_RX Receive
|
|
+ * - dir = DMA_DIR_TX Transmit
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_set_dma(struct ioh_udc_regs __iomem *dev, int dir)
|
|
+{
|
|
+ if (dir == DMA_DIR_RX)
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_RDE);
|
|
+ else if (dir == DMA_DIR_TX)
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, (1 << UDC_DEVCTL_TDE));
|
|
+
|
|
+}
|
|
+
|
|
+/* Disable RX/TX DMA */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_clear_dma(struct ioh_udc_regs __iomem *dev, int dir)
|
|
+ * @remarks Clear the 'TDE' or RDE bit of device control
|
|
+ * register depending on the direction specified
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param dir [IN] whether Tx or Rx
|
|
+ * - dir = DMA_DIR_RX Receive
|
|
+ * - dir = DMA_DIR_TX Transmit
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_clear_dma(struct ioh_udc_regs __iomem *dev, int dir)
|
|
+{
|
|
+ if (dir == DMA_DIR_RX)
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_RDE); /*
|
|
+ clear RDE */
|
|
+ else if (dir == DMA_DIR_TX)
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_TDE);
|
|
+
|
|
+}
|
|
+
|
|
+/* Set CSR done */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_set_csr_done(struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks Set the device control register CSR done field (bit 13)
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_set_csr_done(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ /* set CSR Done */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, 1 << UDC_DEVCTL_CSR_DONE);
|
|
+}
|
|
+
|
|
+/* Set Burst length */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_set_burst_length(struct ioh_udc_regs __iomem *dev, u8 len)
|
|
+ * @remarks The main tasks done by this method are:
|
|
+ * - Set the device control register burst length field
|
|
+ * - Enable the bust mode
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param len [IN] burst length
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_set_burst_length(struct ioh_udc_regs __iomem *dev, u8 len)
|
|
+{
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, (0xff << UDC_DEVCTL_BRLEN_OFS));
|
|
+ /* set Burst length and enable burst mode*/
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, (len << UDC_DEVCTL_BRLEN_OFS) |
|
|
+ (1 << UDC_DEVCTL_BREN));
|
|
+}
|
|
+
|
|
+/* Set Threshold length */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_set_threshold_length(struct ioh_udc_regs __iomem *dev, u8 len)
|
|
+ * @remarks - Set the device control register threshold
|
|
+ * length field
|
|
+ * - Enable the threshold mode
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param len [IN] burst length
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_set_threshold_length(struct ioh_udc_regs __iomem *dev, u8 len)
|
|
+{
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devctl, (0xff << UDC_DEVCTL_THLEN_OFS));
|
|
+ /* set Burst Threshold length and enable threshold mode*/
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devctl, (len << UDC_DEVCTL_THLEN_OFS) |
|
|
+ (1 << UDC_DEVCTL_THE));
|
|
+}
|
|
+
|
|
+/* Disable device interrupts */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_disable_interrupts(struct ioh_udc_regs __iomem *dev, u32 mask)
|
|
+ * @remarks Disables the specified interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param mask [IN] mask to disable interrupts
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_disable_interrupts(struct ioh_udc_regs __iomem *dev, u32 mask)
|
|
+{
|
|
+ /* set the mask */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->devirqmsk, mask);
|
|
+ IOH_DEBUG("Interrupt mask reg = %08x",
|
|
+ IOH_READ32((u32)&dev->devirqmsk));
|
|
+}
|
|
+
|
|
+/* Enable device interrupts */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_enable_interrupts(struct ioh_udc_regs __iomem *dev,
|
|
+ * u32 mask)
|
|
+ * @remarks Enable the specified interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param mask [IN] mask to enable interrupts
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_enable_interrupts(struct ioh_udc_regs __iomem *dev, u32 mask)
|
|
+{
|
|
+ /* set the mask */
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->devirqmsk, mask);
|
|
+ IOH_DEBUG("Interrupt mask reg = %08x",
|
|
+ IOH_READ32((u32)&dev->devirqmsk));
|
|
+}
|
|
+
|
|
+/* Disable Ep interrupts */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_disable_ep_interrupts(struct ioh_udc_regs __iomem *dev,
|
|
+ * u32 mask)
|
|
+ * @remarks Disable endpoint interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param mask [IN] mask to disable interrupts
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_disable_ep_interrupts(struct ioh_udc_regs __iomem *dev, u32 mask)
|
|
+{
|
|
+ /* set the mask */
|
|
+ IOH_SET_ADDR_BIT((u32)&dev->epirqmsk, mask);
|
|
+ IOH_DEBUG("Interrupt ep mask reg = %08x",
|
|
+ IOH_READ32((u32)&dev->epirqmsk));
|
|
+}
|
|
+
|
|
+/* Enable Ep interrupts */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_enable_ep_interrupts(struct ioh_udc_regs __iomem *dev,
|
|
+ * u32 mask)
|
|
+ * @remarks Enable endpoint interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param mask [IN] mask to enable interrupts
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_enable_ep_interrupts(struct ioh_udc_regs __iomem *dev, u32 mask)
|
|
+{
|
|
+ /* set the mask */
|
|
+ IOH_CLR_ADDR_BIT((u32)&dev->epirqmsk, mask);
|
|
+ IOH_DEBUG("Interrupt ep mask reg = %08x",
|
|
+ IOH_READ32((u32)&dev->epirqmsk));
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_read_device_interrupts(struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks Read the device interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @retval u32
|
|
+ * - The device interrupts
|
|
+ */
|
|
+INLINE u32 ioh_udc_read_device_interrupts(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ return IOH_READ32((u32)&dev->devirqsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_write_device_interrupts
|
|
+ * (struct ioh_udc_regs __iomem *dev, u32 val)
|
|
+ * @remarks Write device interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param val [IN] the value to be written to interrupt register
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_write_device_interrupts(struct ioh_udc_regs __iomem *dev,
|
|
+ u32 val)
|
|
+{
|
|
+ IOH_WRITE32(val, (u32)&dev->devirqsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_read_ep_interrupts(struct ioh_udc_regs __iomem *dev)
|
|
+ * @remarks Read the endpoint interrupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @retval u32
|
|
+ * - the endpoint interrupt
|
|
+ */
|
|
+INLINE u32 ioh_udc_read_ep_interrupts(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ return IOH_READ32((u32)&dev->epirqsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_write_ep_interrupts
|
|
+ * @remarks Clear endpoint interupts
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @param val [IN] the value to be written to interrupt register
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_write_ep_interrupts(struct ioh_udc_regs __iomem *dev,
|
|
+ u32 val)
|
|
+{
|
|
+ IOH_WRITE32(val, (u32)&dev->epirqsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_read_device_status
|
|
+ * @remarks Read the device status
|
|
+ * @param dev [IN] reference to structure of type ioh_udc_regs
|
|
+ * @retval u32
|
|
+ * - the device status
|
|
+ */
|
|
+INLINE u32 ioh_udc_read_device_status(struct ioh_udc_regs __iomem *dev)
|
|
+{
|
|
+ return IOH_READ32((u32)&dev->devsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_read_ep_control(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Read the endpoint control
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval u32
|
|
+ * - the endpoint control register value
|
|
+ */
|
|
+INLINE u32 ioh_udc_read_ep_control(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ return IOH_READ32((u32)&ep->epctl);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_clear_ep_control(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Clear the endpoint control register
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval u32
|
|
+ * - the endpoint control register value
|
|
+ */
|
|
+INLINE void ioh_udc_clear_ep_control(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ return IOH_WRITE32(0, (u32)&ep->epctl);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_read_ep_status(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Read the endpoint status
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval u32
|
|
+ * - the endpoint status
|
|
+ */
|
|
+INLINE u32 ioh_udc_read_ep_status(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ return IOH_READ32((u32)&ep->epsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_clear_ep_status(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ * u32 stat)
|
|
+ * @remarks Clear the endpoint status
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @param stat [IN] endpoint status
|
|
+ * @retval none
|
|
+ */
|
|
+INLINE void ioh_udc_clear_ep_status(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 stat)
|
|
+{
|
|
+ return IOH_WRITE32(stat, (u32)&ep->epsts);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_set_nak(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Set the bit 7 (SNAK field) of the endpoint control register
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_set_nak(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_SNAK);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_clear_nak(struct ioh_udc_ep_regs __iomem *ep)
|
|
+ * @remarks Set the bit 8 (CNAK field) of the endpoint control register
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_clear_nak(struct ioh_udc_ep_regs __iomem *ep)
|
|
+{
|
|
+ unsigned int loopcnt = 0;
|
|
+
|
|
+ if (IOH_READ32((u32)&ep->epctl) & (1 << UDC_EPCTL_NAK)) {
|
|
+ if (!(EP_IS_IN(ep))) {
|
|
+ while ((ioh_udc_read_ep_status(ep) &
|
|
+ (1 << UDC_EPSTS_MRXFIFO_EMP)) == 0) {
|
|
+ if (loopcnt++ > 100000) {
|
|
+ IOH_DEBUG("RxFIFO not Empty loop \
|
|
+ count = %d", loopcnt);
|
|
+ break;
|
|
+ }
|
|
+ udelay(100);
|
|
+ }
|
|
+ }
|
|
+ while (IOH_READ32((u32)&ep->epctl) & (1 << UDC_EPCTL_NAK)) {
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_CNAK);
|
|
+ udelay(5);
|
|
+ if (loopcnt++ >= 25) {
|
|
+ IOH_DEBUG("Clear NAK not set for ep%d%s:\
|
|
+ counter=%d",
|
|
+ EP_NUM(ep), (EP_IS_IN(ep) ? \
|
|
+ "in" : "out"), loopcnt);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_fifo_flush(struct ioh_udc_ep_regs __iomem *ep, int dir)
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ *- If the endpoint is IN,
|
|
+ * - set the bit 1 (F) of endpoint control register
|
|
+ *- Otherwise
|
|
+ * - If the Rx FIFO is not empty,
|
|
+ * - Set bit 12 (MRX FLUSH) of EP control register
|
|
+ * - Wait till MRXFIFO EMPTY (bit 8) is set in EP status register
|
|
+ * - Clear bit 12 (MRX FLUSH) of EP control register
|
|
+ * @param ep [IN] reference to structure of type ioh_udc_ep_regs
|
|
+ * @param dir [IN] direction of endpoint
|
|
+ * - dir = 0 endpoint is OUT
|
|
+ * - dir != 0 endpoint is IN
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_fifo_flush(struct ioh_udc_ep_regs __iomem *ep, int dir)
|
|
+{
|
|
+ unsigned int loopcnt = 0;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_ep_fifo_flush: ep%d%s", EP_NUM(ep),
|
|
+ (EP_IS_IN(ep) ? "in" : "out"));
|
|
+ if (dir) { /* IN ep */
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 << UDC_EPCTL_F);
|
|
+ } else {
|
|
+ if ((ioh_udc_read_ep_status(ep) &
|
|
+ (1 << UDC_EPSTS_MRXFIFO_EMP)) == 0) {
|
|
+ IOH_SET_ADDR_BIT((u32)&ep->epctl, 1 <<
|
|
+ UDC_EPCTL_MRXFLUSH);
|
|
+ /* Wait for RxFIFO Empty */
|
|
+ while ((ioh_udc_read_ep_status(ep) &
|
|
+ (1 << UDC_EPSTS_MRXFIFO_EMP)) == 0) {
|
|
+ if (loopcnt++ > 1000000) {
|
|
+ IOH_DEBUG("RxFIFO not Empty loop\
|
|
+ count = %d", loopcnt);
|
|
+ break;
|
|
+ }
|
|
+ udelay(100);
|
|
+ }
|
|
+ IOH_CLR_ADDR_BIT((u32)&ep->epctl, 1 <<
|
|
+ UDC_EPCTL_MRXFLUSH);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Enables endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_enable(struct ioh_udc_ep_regs __iomem *regs,
|
|
+ * struct ioh_udc_cfg_data *cfg,
|
|
+ * const struct usb_endpoint_descriptor *desc)
|
|
+ * @remarks The following actions are performed:
|
|
+ *- Set transfer type of the endpoint using ioh_udc_ep_set_trfr_type
|
|
+ * API passing desc->bmAttributes
|
|
+ *- Set Buffer size for the endpoint by ioh_udc_ep_set_bufsz
|
|
+ *- Set Maximum packet size for the endpoint using ioh_udc_ep_set_maxpkt
|
|
+ * API, passing desc->wMaxPacketSize
|
|
+ *- Flush FIFO of the endpoint by ioh_udc_ep_fifo_flush
|
|
+ *- Set NAK of the endpoint by ioh_udc_ep_set_nak
|
|
+ *- Calculate the endpoint configuration value from the following parameters
|
|
+ * and update the same in corresponding register [based on endpoint
|
|
+ * direction]using ioh_udc_write_csr API:
|
|
+ * - endpoint (ep) number
|
|
+ * - ep direction
|
|
+ * - ep transfer type mask
|
|
+ * - cfg->cur_cfg
|
|
+ * - cfg->cur_intf
|
|
+ * - cfg->cur_intf
|
|
+ * - desc->wMaxPacketSize
|
|
+ * @param regs [IN] Reference to structure ioh_udc_ep_regs
|
|
+ * @param desc [IN] endpoint descriptor
|
|
+ * @retval none
|
|
+ * @see
|
|
+ * - ioh_udc_ep_set_trfr_type
|
|
+ * - ioh_udc_ep_set_bufsz
|
|
+ * - ioh_udc_ep_set_maxpkt
|
|
+ * - ioh_udc_ep_set_nak
|
|
+ * - ioh_udc_ep_fifo_flush
|
|
+ * - ioh_udc_write_csr
|
|
+ */
|
|
+void ioh_udc_ep_enable(struct ioh_udc_ep_regs __iomem *regs,
|
|
+ struct ioh_udc_cfg_data *cfg,
|
|
+ const struct usb_endpoint_descriptor *desc)
|
|
+{
|
|
+ u32 ep_num = EP_NUM(regs);
|
|
+ u32 ep_in = EP_IS_IN(regs);
|
|
+ u32 val = 0;
|
|
+ u32 buff_size = 0;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_ep_enable: ep%x%s bmAttributes = %d\
|
|
+ wMaxPacketSize = %d",
|
|
+ ep_num, (ep_in ? "in" : "out"), desc->bmAttributes,
|
|
+ desc->wMaxPacketSize);
|
|
+ /* set traffic type */
|
|
+ ioh_udc_ep_set_trfr_type(regs, desc->bmAttributes);
|
|
+ /* Set buff size */
|
|
+ if (ep_in)
|
|
+ buff_size = UDC_EPIN_BUFF_SIZE;
|
|
+ else
|
|
+ buff_size = UDC_EPOUT_BUFF_SIZE;
|
|
+
|
|
+ ioh_udc_ep_set_bufsz(regs, buff_size, ep_in);
|
|
+ /* Set max packet size */
|
|
+ ioh_udc_ep_set_maxpkt(regs, le16_to_cpu(desc->wMaxPacketSize));
|
|
+ /* Set NAK */
|
|
+ ioh_udc_ep_set_nak(regs);
|
|
+ /* Flush fifo */
|
|
+ ioh_udc_ep_fifo_flush(regs, ep_in);
|
|
+ /* Configure the endpoint */
|
|
+ val = ep_num << UDC_CSR_NE_NUM_OFS | ep_in << UDC_CSR_NE_DIR_OFS |
|
|
+ ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) <<
|
|
+ UDC_CSR_NE_TYPE_OFS) |
|
|
+ (cfg->cur_cfg << UDC_CSR_NE_CFG_OFS) |
|
|
+ (cfg->cur_intf << UDC_CSR_NE_INTF_OFS) |
|
|
+ (cfg->cur_alt << UDC_CSR_NE_ALT_OFS) |
|
|
+ le16_to_cpu(desc->wMaxPacketSize) <<
|
|
+ UDC_CSR_NE_MAX_PKT_OFS;
|
|
+
|
|
+ if (ep_in)
|
|
+ ioh_udc_write_csr(val, (u32) (ioh_udc_base + UDC_CSR_ADDR +
|
|
+ (ep_num * 2) * 4));
|
|
+ else
|
|
+ ioh_udc_write_csr(val, (u32) (ioh_udc_base + UDC_CSR_ADDR +
|
|
+ (ep_num * 2 + 1) * 4));
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_ep_enable: Endpoint register = 0x%08x", val);
|
|
+}
|
|
+
|
|
+/* Resets endpoint */
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+ * @fn ioh_udc_ep_disable(struct ioh_udc_ep_regs __iomem *regs)
|
|
+ * @remarks The following actions are performed:
|
|
+ *- If the endpoint is IN,
|
|
+ * - Set the bit 1 (Flush field) and clear other bit of endpoint control
|
|
+ * register
|
|
+ * - Set the bit 7 (SNAK field) and clear other bit of the endpoint control
|
|
+ * register
|
|
+ * - Set the bit 6 (IN field) of the endpoint status register
|
|
+ *- Otherwise
|
|
+ * - Set the bit 7 (SNAK field) and clear other bit of the endpoint control
|
|
+ * register
|
|
+ *- Initialize the data descriptor pointer to 0 using
|
|
+ * ioh_udc_ep_set_ddptr API
|
|
+ * @param regs [IN] Reference to structure ioh_udc_ep_regs
|
|
+ * @retval none
|
|
+ */
|
|
+void ioh_udc_ep_disable(struct ioh_udc_ep_regs __iomem *regs)
|
|
+{
|
|
+ if (EP_IS_IN(regs)) {
|
|
+ /* flush the fifo */
|
|
+ IOH_WRITE32(1 << UDC_EPCTL_F , (u32)®s->epctl);
|
|
+ /* set NAK */
|
|
+ IOH_WRITE32(1 << UDC_EPCTL_SNAK , (u32)®s->epctl);
|
|
+
|
|
+ IOH_SET_ADDR_BIT((u32)®s->epsts, 1 << UDC_EPSTS_IN);
|
|
+ } else {
|
|
+ /* set NAK */
|
|
+ IOH_WRITE32(1 << UDC_EPCTL_SNAK , (u32)®s->epctl);
|
|
+ }
|
|
+ /* reset desc pointer */
|
|
+ IOH_WRITE32(0, (u32)®s->desptr);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc_hal.h
|
|
@@ -0,0 +1,1829 @@
|
|
+ /*!
|
|
+ * @file ioh_udc_hal.h
|
|
+ * @brief This file contains the declarations for all HAL layer APIs.
|
|
+ * It also
|
|
+ * lists the various macros used by HAL layer.
|
|
+ *
|
|
+ * The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ * It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or
|
|
+ * interrupt type).
|
|
+ *
|
|
+ * The IOH USB device controller driver provides required interface
|
|
+ * to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ * @version 0.96
|
|
+ *
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * History:
|
|
+ * Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ * modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _IOH_UDC_HAL_H_
|
|
+#define _IOH_UDC_HAL_H_
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+/*! @defgroup UDC */
|
|
+
|
|
+/*! @defgroup UDC_Global
|
|
+@ingroup UDC
|
|
+@brief This group describes the global entities within
|
|
+ the module.
|
|
+@remarks This group includes all the global data structures
|
|
+ used within the modules. These are mainly used to
|
|
+ store the device related information, so that it can
|
|
+ be used by other functions of the modules.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_PCILayer
|
|
+@ingroup UDC
|
|
+@brief This group describes the PCI layer interface
|
|
+ functionalities.
|
|
+@remarks This group contains the functions and data structures
|
|
+ that are used to interface the module with PCI Layer
|
|
+ subsystem of the Kernel.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_InterfaceLayer
|
|
+@ingroup UDC
|
|
+@brief This group describes the Driver interface functionalities.
|
|
+@remarks This group contains the data structures and functions used
|
|
+ to interface the module driver with the kernel subsystem.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_HALLayer
|
|
+@ingroup UDC
|
|
+@brief This group describes the hardware specific functionalities.
|
|
+@remarks This group contains the functions and data structures used
|
|
+ by the module to communicate with the hardware. These
|
|
+ functions are device specific and designed according to the
|
|
+ device specifications.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_Utilities
|
|
+@ingroup UDC
|
|
+@brief This group describes the utility functionalities.
|
|
+@remarks This group contains the functions and data structures used
|
|
+ to assist the other functionalities in their operations.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_PCILayerAPI
|
|
+@ingroup UDC_PCILayer
|
|
+@brief This group contains the API(functions) used as the PCI
|
|
+ interface between the Kernel subsystem and the module.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_PCILayerFacilitators
|
|
+@ingroup UDC_PCILayer
|
|
+@brief This group contains the data structures used by the PCI
|
|
+ Layer APIs for their functionalities.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_InterfaceLayerAPI
|
|
+@ingroup UDC_InterfaceLayer
|
|
+@brief This group contains the API(functions) used as the Driver
|
|
+
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_InterfaceLayerFacilitators
|
|
+@ingroup UDC_InterfaceLayer
|
|
+@brief This group contains the data structures used by the Driver
|
|
+ interface APIs for their functionalities.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_HALLayerAPI
|
|
+@ingroup UDC_HALLayer
|
|
+@brief This group contains the APIs(functions) used to interact with
|
|
+ the hardware. These APIs act as an interface between the
|
|
+ hardware and the other driver functions.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+/*! @defgroup UDC_UtilitiesAPI
|
|
+@ingroup UDC_Utilities
|
|
+@brief This group contains the APIs(functions) used by other functions
|
|
+ in their operations.
|
|
+<hr>
|
|
+*/
|
|
+
|
|
+
|
|
+/* Device Config Register */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_ADDR
|
|
+@brief Address offset of Device Configuration Register
|
|
+*/
|
|
+#define UDC_DEVCFG_ADDR 0x400
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SET_DESC
|
|
+@brief Bit position of SET_DESC field in
|
|
+ Device configuration register
|
|
+*/
|
|
+#define UDC_DEVCFG_SET_DESC 18
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_CSR_PRG
|
|
+@brief Bit position of CSR_PRG field in
|
|
+ Device configuration register
|
|
+*/
|
|
+
|
|
+#define UDC_DEVCFG_CSR_PRG 17
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_HALT_STATUS
|
|
+@brief Bit position of HALT_STATUS field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_HALT_STATUS 16
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_STATUS1
|
|
+@brief Bit position of STATUS1 field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_STATUS1 8
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_STATUS
|
|
+@brief Bit position of STATUS field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_STATUS 7
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_DIR
|
|
+@brief Bit position of DIR field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_DIR 6
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_PI
|
|
+@brief Bit position of PI field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_PI 5
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SS
|
|
+@brief Bit position of SS field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_SS 4
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SP
|
|
+@brief Bit position of SP field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_SP 3
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_RWKP
|
|
+@brief Bit position of RWKP field in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_RWKP 2
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SPD_MASK
|
|
+@brief Mask for SPD bits in
|
|
+ Device configuration register.
|
|
+*/
|
|
+#define UDC_DEVCFG_SPD_MASK 0x3
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SPD_OFS
|
|
+*/
|
|
+#define UDC_DEVCFG_SPD_OFS 0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SPD_HS
|
|
+*/
|
|
+#define UDC_DEVCFG_SPD_HS 0x0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SPD_FS
|
|
+*/
|
|
+#define UDC_DEVCFG_SPD_FS 0x1
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCFG_SPD_LS
|
|
+*/
|
|
+#define UDC_DEVCFG_SPD_LS 0x2
|
|
+/*#define UDC_DEVCFG_SPD_FS 0x3*/
|
|
+
|
|
+/* Device Control Register */
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_ADDR
|
|
+@brief Address offset for Device control register
|
|
+*/
|
|
+#define UDC_DEVCTL_ADDR 0x404
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_THLEN_MASK
|
|
+@brief Mask for Threshold length field of
|
|
+ Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_THLEN_MASK 0xff000000
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_THLEN_OFS
|
|
+*/
|
|
+#define UDC_DEVCTL_THLEN_OFS 24
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_BRLEN_MASK
|
|
+@brief Mask for Burst length field of
|
|
+ Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_BRLEN_OFS
|
|
+@brief Starting bit position of
|
|
+ Burst length field in Device
|
|
+ control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_BRLEN_OFS 16
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_CSR_DONE
|
|
+@brief The bit position of CSR_DONE
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_CSR_DONE 13
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_DEVNAK
|
|
+@brief The bit position of DEVNAK
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_DEVNAK 12
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_SD
|
|
+@brief The bit position of SD field in
|
|
+ Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_SD 10
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_MODE
|
|
+@brief The bit position of MODE
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_MODE 9
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_BREN
|
|
+@brief The bit position of BREN
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_BREN 8
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_THE
|
|
+@brief The bit position of THE
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_THE 7
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_BF
|
|
+@brief The bit position of BF
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_BF 6
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_BE
|
|
+@brief The bit position of BE
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_BE 5
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_DU
|
|
+@brief The bit position of DU
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_DU 4
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_TDE
|
|
+@brief The bit position of TDE
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_TDE 3
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_RDE
|
|
+@brief The bit position of RDE
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_RDE 2
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVCTL_RES
|
|
+@brief The bit position of RES
|
|
+ field in Device control register.
|
|
+*/
|
|
+#define UDC_DEVCTL_RES 0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def IOH_UDC_BRLEN
|
|
+@brief Specifies the Burst Length
|
|
+*/
|
|
+#define IOH_UDC_BRLEN 0xF /* Burst length */
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def IOH_UDC_THLEN
|
|
+@brief Specifies the Threshold Length
|
|
+*/
|
|
+#define IOH_UDC_THLEN 0x1F /* Threshold length */
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def DMA_DIR_RX
|
|
+@brief Specifies DMA for data receive
|
|
+*/
|
|
+#define DMA_DIR_RX 1
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def DMA_DIR_TX
|
|
+@brief Specifies DMA for data transmit
|
|
+*/
|
|
+#define DMA_DIR_TX 2
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DMA_MAXPACKET
|
|
+@brief Specifies maximum packet size for DMA
|
|
+*/
|
|
+#define UDC_DMA_MAXPACKET 65536
|
|
+
|
|
+/* Device Status Register */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ADDR
|
|
+@brief The address offset for Device Status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_ADDR 0x408
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_TS_MASK
|
|
+@brief Mask for TS field of
|
|
+ Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_TS_MASK 0xfffc0000
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_TS_OFS
|
|
+@brief Starting bit position of TS field of
|
|
+ Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_TS_OFS 18
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_RWKPST
|
|
+@brief Bit position of RWKPST field of
|
|
+ Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_RWKPST 17
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_PHY_ERROR
|
|
+@brief Bit position of PHY_ERROR field of
|
|
+ Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_PHY_ERROR 16
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_RXFIFO_EMPTY
|
|
+@brief Bit position of RXFIFO_EMPTY field of
|
|
+ Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_RXFIFO_EMPTY 15
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ENUM_SPEED_MASK
|
|
+@brief Mask for SPEED field of
|
|
+ Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ENUM_SPEED_OFS
|
|
+@brief Starting bit position of SPEED field
|
|
+ of Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_ENUM_SPEED_OFS 13
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ENUM_SPEED_FULL
|
|
+@brief Specifies value for maximum speed for
|
|
+ SPEED field of Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_ENUM_SPEED_FULL 1
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ENUM_SPEED_HIGH
|
|
+@brief Specifies value for high speed for
|
|
+ SPEED field of Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_ENUM_SPEED_HIGH 0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ENUM_SPEED_LOW
|
|
+@brief Specifies value for low speed for
|
|
+ SPEED field of Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_ENUM_SPEED_LOW 2
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ENUM_SPEED_FULLX
|
|
+@brief Specifies value for full speed for
|
|
+ SPEED field of Device status register
|
|
+*/
|
|
+#define UDC_DEVSTS_ENUM_SPEED_FULLX 3
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_SUSP
|
|
+@brief Bit position of SUSP field of
|
|
+ Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_SUSP 12
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ALT_MASK
|
|
+@brief Mask for ALT field of
|
|
+ Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_ALT_MASK 0x00000f00
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_ALT_OFS
|
|
+@brief Starting bit position of
|
|
+ ALT field of Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_ALT_OFS 8
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_INTF_MASK
|
|
+@brief Mask for INTF field of
|
|
+ Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_INTF_MASK 0x000000f0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_INTF_OFS
|
|
+@brief The starting bit position for INTF field
|
|
+ in Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_INTF_OFS 4
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_CFG_MASK
|
|
+@brief Mask for CFG field in
|
|
+ Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_CFG_MASK 0x0000000f
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVSTS_CFG_OFS
|
|
+@brief Starting bit position for CFG field
|
|
+ in Device status register.
|
|
+*/
|
|
+#define UDC_DEVSTS_CFG_OFS 0
|
|
+
|
|
+
|
|
+/* Device Interrupt Register */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_ADDR
|
|
+@brief The address offset for
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_ADDR 0x40c
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_RWKP
|
|
+@brief The bit position for
|
|
+ RWKP field in Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_RWKP 7
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_ENUM
|
|
+@brief Bit position for ENUM field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_ENUM 6
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_SOF
|
|
+@brief Bit position for SOF field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_SOF 5
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_US
|
|
+@brief Bit position for US field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_US 4
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_UR
|
|
+@brief Bit position for UR field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_UR 3
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_ES
|
|
+@brief Bit position for ES field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_ES 2
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_SI
|
|
+@brief Bit position for SI field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_SI 1
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_SC
|
|
+@brief Bit position for SC field in
|
|
+ Device interrupt register
|
|
+*/
|
|
+#define UDC_DEVINT_SC 0
|
|
+
|
|
+/* Device Interrupt Mask Register */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_MASK_ADDR
|
|
+@brief The address offset for
|
|
+ Device interrupt mask register
|
|
+*/
|
|
+#define UDC_DEVINT_MSK_ADDR 0x410
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEVINT_MSK
|
|
+@brief Interrupt Mask
|
|
+*/
|
|
+#define UDC_DEVINT_MSK 0x7f
|
|
+
|
|
+/* Endpoint Interrupt Register */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_ADDR
|
|
+@brief The address offset for
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_ADDR 0x414
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_MASK
|
|
+@brief Mask for OUT field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_MASK 0xffff0000
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_OFS
|
|
+@brief Starting bit position for OUT field of
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_OFS 16
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_MASK
|
|
+@brief Mask for IN field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_IN_MASK 0x0000ffff
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_OFS
|
|
+@brief Starting bit position for IN field of
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_IN_OFS 0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_EP0
|
|
+@brief Bit position for IN_EP0 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_IN_EP0 0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_EP1
|
|
+@brief Bit position for IN_EP1 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_IN_EP1 1
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_EP2
|
|
+@brief Bit position for IN_EP2 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_IN_EP2 2
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_EP3
|
|
+@brief Bit position for IN_EP3 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_IN_EP3 3
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_EP0
|
|
+@brief Bit position for OUT_EP0 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_EP0 16
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_EP1
|
|
+@brief Bit position for OUT_EP1 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_EP1 17
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_EP2
|
|
+@brief Bit position for OUT_EP2 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_EP2 18
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_EP3
|
|
+@brief Bit position for OUT_EP3 field in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_EP3 19
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_EP0_ENABLE_MSK
|
|
+@brief Mask to enable EP0 interrupt in
|
|
+ Endpoint interrupt register
|
|
+*/
|
|
+#define UDC_EPINT_EP0_ENABLE_MSK 0x000e000e
|
|
+
|
|
+/* Endpoint Interrupt Mask Register -----------------------------------------*/
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_MSK_ADDR
|
|
+@brief Address offset for Endpoint Interrupt Mask Register
|
|
+*/
|
|
+#define UDC_EPINT_MSK_ADDR 0x418
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_MSK_MASK
|
|
+@brief Mask for OUT_MSK field of
|
|
+ Endpoint Interrupt Mask Register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_MSK_MASK 0xffff0000
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_OUT_MSK_OFS
|
|
+@brief Starting bit position for OUT_MSK field of
|
|
+ Endpoint Interrupt Mask Register
|
|
+*/
|
|
+#define UDC_EPINT_OUT_MSK_OFS 16
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_MSK_MASK
|
|
+@brief Mask for IN_MSK field of
|
|
+ Endpoint Interrupt Mask Register
|
|
+*/
|
|
+#define UDC_EPINT_IN_MSK_MASK 0x0000ffff
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_MSK_MASK
|
|
+@brief Starting bit position for IN_MSK field of
|
|
+ Endpoint Interrupt Mask Register
|
|
+*/
|
|
+#define UDC_EPINT_IN_MSK_OFS 0
|
|
+
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPINT_IN_MSK_MASK
|
|
+@brief Mask to disable all interrupts in
|
|
+ Endpoint Interrupt Mask Register
|
|
+*/
|
|
+#define UDC_EPINT_MSK_DISABLE_ALL 0xffffffff
|
|
+
|
|
+/* mask non-EP0 endpoints */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_EPDATAINT_MSK_DISABLE
|
|
+@brief mask non-EP0 endpoints
|
|
+*/
|
|
+#define UDC_EPDATAINT_MSK_DISABLE 0xfffefffe
|
|
+
|
|
+/* mask all dev interrupts */
|
|
+/*! @ingroup UDC_HALLayer
|
|
+@def UDC_DEV_MSK_DISABLE
|
|
+@brief mask all dev interrupts
|
|
+*/
|
|
+#define UDC_DEV_MSK_DISABLE 0x7f
|
|
+
|
|
+/* UDC CSR Busy status Register -----------------------------------------*/
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_CSR_BUSY_ADDR
|
|
+@brief Address offset for UDC CSR Busy status Register
|
|
+*/
|
|
+#define IOH_UDC_CSR_BUSY_ADDR 0x4f0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_CSR_BUSY
|
|
+@brief Bit position of CSR field in
|
|
+ UDC CSR Busy status Register
|
|
+*/
|
|
+#define IOH_UDC_CSR_BUSY 1
|
|
+
|
|
+/* SOFT RESET Register ------------------------------------------------------*/
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_SRST_ADDR
|
|
+@brief Address offset for UDC Soft reset Register
|
|
+*/
|
|
+#define IOH_UDC_SRST_ADDR 0x4fc
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_PHY_RESET
|
|
+@brief Bit position of PSRST field in
|
|
+ UDC Soft Reset Register
|
|
+*/
|
|
+#define IOH_UDC_PSRST 1
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_SOFT_RESET
|
|
+@brief Bit position of SRST field in
|
|
+ UDC Soft Reset Register
|
|
+*/
|
|
+#define IOH_UDC_SRST 0
|
|
+
|
|
+/* Endpoint-specific CSR's --------------------------------------------------*/
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPREGS_ADDR
|
|
+@brief address offset for Endpoint-specific CSR
|
|
+*/
|
|
+#define UDC_EPREGS_ADDR 0x0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN_REGS_ADDR
|
|
+@brief address offset for EPIN register
|
|
+*/
|
|
+#define UDC_EPIN_REGS_ADDR 0x0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_REGS_ADDR
|
|
+@brief address offset for EPOUT register
|
|
+*/
|
|
+#define UDC_EPOUT_REGS_ADDR 0x200
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def EP_IS_IN(ep)
|
|
+*/
|
|
+#define EP_IS_IN(ep) (((u32)(ep)) < (ioh_udc_base + UDC_EPOUT_REGS_ADDR))
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def EP_NUM(ep)
|
|
+*/
|
|
+#define EP_NUM(ep) ((((u32)(ep) - (ioh_udc_base +\
|
|
+ UDC_EPREGS_ADDR)) / 0x20) & 0xf)
|
|
+
|
|
+/* Endpoint Control Registers */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ADDR
|
|
+@brief Address offset for Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ADDR 0x0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_RRDY
|
|
+@brief Bit position of RRDY field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_RRDY 9
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_CNAK
|
|
+@brief Bit position of CNAK field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_CNAK 8
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_SNAK
|
|
+@brief Bit position of SNAK field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_SNAK 7
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_NAK
|
|
+@brief Bit position of NAK field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_NAK 6
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ET_MASK
|
|
+@brief Mask for ET field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ET_MASK 0x00000030
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ET_OFS
|
|
+@brief Starting bit position for ET field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ET_OFS 4
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ET_CONTROL
|
|
+@brief Value for ET field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ET_CONTROL 0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ET_ISO
|
|
+@brief Value for ET field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ET_ISO 1
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ET_BULK
|
|
+@brief Value for ET field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ET_BULK 2
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_ET_INTERRUPT
|
|
+@brief Value for ET field in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_ET_INTERRUPT 3
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_MRXFLUSH
|
|
+@brief Bit position for MRXFLUSH in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_MRXFLUSH 12
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_P
|
|
+@brief Bit position for P in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_P 3
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_SN
|
|
+@brief Bit position for SN in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_SN 2
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_F
|
|
+@brief Bit position for F in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_F 1
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPCTL_S
|
|
+@brief Bit position for S in
|
|
+ Endpoint Control Register
|
|
+*/
|
|
+#define UDC_EPCTL_S 0
|
|
+
|
|
+/* Endpoint Status Registers */
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_ADDR
|
|
+@brief Address offset for Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_ADDR 0x4
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_XFERDONE
|
|
+@brief Bit position for XFERDONE in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_XFERDONE 27
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_RSS
|
|
+@brief Bit position for RSS in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_RSS 26
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_RCS
|
|
+@brief Bit position for RCS in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_RCS 25
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_TXEMPTY
|
|
+@brief Bit position for TXEMPTY in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_TXEMPTY 24
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_ISOINDONE
|
|
+@brief Bit position for ISOINDONE in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_ISOINDONE 23
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_RX_PKT_SIZE_MASK
|
|
+@brief Mask for RX_PKT_SIZE field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_RX_PKT_SIZE_MASK 0x007ff800
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_RX_PKT_SIZE_OFS
|
|
+@brief Starting offset for RX_PKT_SIZE field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_RX_PKT_SIZE_OFS 11
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_TDC
|
|
+@brief Bit position for TDC field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_TDC 10
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_HE
|
|
+@brief Bit position for HE field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_HE 9
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_MRXFIFO_EMP
|
|
+@brief Bit position for MRXFIFO EMPTY field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_MRXFIFO_EMP 8
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_BNA
|
|
+@brief Bit position for BNA field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_BNA 7
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_IN
|
|
+@brief Bit position for IN field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_IN 6
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_MASK
|
|
+@brief Mask for OUT field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_MASK 0x00000030
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_OFS
|
|
+@brief Starting bit position for OUT field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_OFS 4
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_DATA
|
|
+@brief Value for OUT field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_DATA 1
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_DATA_CLEAR
|
|
+@brief Clear OUT field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_DATA_CLEAR 0x10
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_DATA_CLEAR
|
|
+@brief Clear OUT field in
|
|
+ Endpoint Status Register
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_SETUP 2
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_DATA_SETUP_CLEAR
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_SETUP_CLEAR 0x20
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPSTS_OUT_CLEAR
|
|
+*/
|
|
+#define UDC_EPSTS_OUT_CLEAR 0x30
|
|
+
|
|
+/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT Registers */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN_BUFF_SIZE_ADDR
|
|
+@brief Address offset for Endpoint Buffer Size IN register
|
|
+*/
|
|
+#define UDC_EPIN_BUFF_SIZE_ADDR 0x8
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_FRAME_NUMBER_ADDR
|
|
+@brief Address offset for Receive Packet Frame Number OUT Register
|
|
+*/
|
|
+#define UDC_EPOUT_FRAME_NUMBER_ADDR 0x8
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN_BUFF_SIZE_MASK
|
|
+@brief Mask for EPIN buffer size field in
|
|
+ Endpoint Buffer Size IN register
|
|
+*/
|
|
+#define UDC_EPIN_BUFF_SIZE_MASK 0x0000ffff
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN_BUFF_SIZE_OFS
|
|
+@brief Starting bit position for EPIN buffer size field in
|
|
+ Endpoint Buffer Size IN register
|
|
+*/
|
|
+#define UDC_EPIN_BUFF_SIZE_OFS 0
|
|
+/* EP0in txfifo = 256 bytes*/
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP0IN_BUFF_SIZE
|
|
+@brief EPIN0 Buffer Size
|
|
+*/
|
|
+#define UDC_EP0IN_BUFF_SIZE 64
|
|
+
|
|
+/* EP0in fullspeed txfifo = 128 bytes*/
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_FS_EPIN0_BUFF_SIZE
|
|
+@brief EPIN0 Buffer Size
|
|
+*/
|
|
+#define UDC_FS_EPIN0_BUFF_SIZE 32
|
|
+
|
|
+/* fifo size mult = fifo size / max packet */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN0_BUFF_SIZE_MULT
|
|
+*/
|
|
+#define UDC_EPIN_BUFF_SIZE_MULT 2
|
|
+
|
|
+/* EPin data fifo size = 2048 bytes DOUBLE BUFFERING */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN_BUFF_SIZE
|
|
+@brief Buffer size
|
|
+*/
|
|
+#define UDC_EPIN_BUFF_SIZE 512
|
|
+/* EPin small INT data fifo size = 128 bytes */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPIN_SMALLINT_BUFF_SIZE
|
|
+@brief Buffer size
|
|
+*/
|
|
+#define UDC_EPIN_SMALLINT_BUFF_SIZE 32
|
|
+
|
|
+/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_FS_EPIN_BUFF_SIZE
|
|
+@brief Buffer size
|
|
+*/
|
|
+#define UDC_FS_EPIN_BUFF_SIZE 32
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_FRAME_NUMBER_MASK
|
|
+@brief Mask for frame number
|
|
+*/
|
|
+#define UDC_EPOUT_FRAME_NUMBER_MASK 0x0000ffff
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_FRAME_NUMBER_OFS
|
|
+@brief Starting bit position for frame number
|
|
+*/
|
|
+#define UDC_EPOUT_FRAME_NUMBER_OFS 0
|
|
+
|
|
+/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_BUFF_SIZE_ADDR
|
|
+@brief Address offset for Endpoint Buffer Size OUT Register
|
|
+*/
|
|
+#define UDC_EPOUT_BUFF_SIZE_ADDR 0x0c
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP_MAX_PKT_SIZE_ADDR
|
|
+@brief Address offset for Max Packet Size Register
|
|
+*/
|
|
+#define UDC_EP_MAX_PKT_SIZE_ADDR 0x0c
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_BUFF_SIZE_MASK
|
|
+@brief Mask for EPOUT buffer size field
|
|
+*/
|
|
+#define UDC_EPOUT_BUFF_SIZE_MASK 0xffff0000
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_BUFF_SIZE_OFS
|
|
+@brief Starting bit position for EPOUT buffer size field
|
|
+*/
|
|
+#define UDC_EPOUT_BUFF_SIZE_OFS 16
|
|
+
|
|
+/* EP0out rxfifo = 256 bytes*/
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP0OUT_BUFF_SIZE
|
|
+@brief EP0 OUT Buffer Size
|
|
+*/
|
|
+#define UDC_EP0OUT_BUFF_SIZE 64
|
|
+
|
|
+/* EPout data fifo size = 2048 bytes DOUBLE BUFFERING */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EPOUT_BUFF_SIZE
|
|
+@brief Buffer size
|
|
+*/
|
|
+#define UDC_EPOUT_BUFF_SIZE 512
|
|
+
|
|
+/* EPout fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_FS_EPOUT_BUFF_SIZE
|
|
+@brief Buffer size
|
|
+*/
|
|
+#define UDC_FS_EPOUT_BUFF_SIZE 32
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP_MAX_PKT_SIZE_MASK
|
|
+@brief Mask for EP maximum packet size
|
|
+*/
|
|
+#define UDC_EP_MAX_PKT_SIZE_MASK 0x0000ffff
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP_MAX_PKT_SIZE_OFS
|
|
+@brief Starting bit position for EP maximum packet size
|
|
+*/
|
|
+#define UDC_EP_MAX_PKT_SIZE_OFS 0
|
|
+/* EP0in max packet size = 64 bytes */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP0IN_MAX_PKT_SIZE
|
|
+@brief EP0 IN maximum packet size
|
|
+*/
|
|
+#define UDC_EP0IN_MAX_PKT_SIZE 64
|
|
+/* EP0out max packet size = 64 bytes */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP0OUT_MAX_PKT_SIZE
|
|
+@brief EP0 OUT maximum packet size
|
|
+*/
|
|
+#define UDC_EP0OUT_MAX_PKT_SIZE 64
|
|
+
|
|
+/* Bulk max packet size = 512 bytes */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_BULK_MAX_PKT_SIZE
|
|
+@brief BULK maximum packet size
|
|
+*/
|
|
+#define UDC_BULK_MAX_PKT_SIZE 512
|
|
+
|
|
+/* EP0in fullspeed max packet size = 64 bytes */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_FS_EP0IN_MAX_PKT_SIZE
|
|
+@brief Full speed EP0 IN maximum packet size
|
|
+*/
|
|
+#define UDC_FS_EP0IN_MAX_PKT_SIZE 64
|
|
+/* EP0out fullspeed max packet size = 64 bytes */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_FS_EP0OUT_MAX_PKT_SIZE
|
|
+@brief Full speed EP0 OUT maximum packet size
|
|
+*/
|
|
+#define UDC_FS_EP0OUT_MAX_PKT_SIZE 64
|
|
+
|
|
+/* Offset to next EP registers */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP_REG_OFS
|
|
+@brief Offset to next EP registers
|
|
+*/
|
|
+#define UDC_EP_REG_OFS 0x20
|
|
+
|
|
+/* UDC_Global CSR's */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_ADD
|
|
+@brief Offset of CSR register
|
|
+*/
|
|
+#define UDC_CSR_ADDR 0x500
|
|
+
|
|
+/* EP NE bits */
|
|
+/* EP number */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_NUM_MASK
|
|
+*/
|
|
+#define UDC_CSR_NE_NUM_MASK 0x0000000f
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_NUM_OFS
|
|
+*/
|
|
+#define UDC_CSR_NE_NUM_OFS 0
|
|
+
|
|
+/* EP direction */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_DIR_MASK
|
|
+@brief EP direction mask
|
|
+*/
|
|
+#define UDC_CSR_NE_DIR_MASK 0x00000010
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_DIR_OFS
|
|
+@brief Offset for EP direction bits
|
|
+*/
|
|
+#define UDC_CSR_NE_DIR_OFS 4
|
|
+
|
|
+/* EP type */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_TYPE_MASK
|
|
+@brief Mask for EP type
|
|
+*/
|
|
+#define UDC_CSR_NE_TYPE_MASK 0x00000060
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_TYPE_OFS
|
|
+@brief Offset for EP type bits
|
|
+*/
|
|
+#define UDC_CSR_NE_TYPE_OFS 5
|
|
+
|
|
+/* EP config number */
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_CFG_MASK
|
|
+@brief Mask for EP config number
|
|
+*/
|
|
+#define UDC_CSR_NE_CFG_MASK 0x00000780
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_CFG_OFS
|
|
+@brief Offset for EP config number bits
|
|
+*/
|
|
+#define UDC_CSR_NE_CFG_OFS 7
|
|
+
|
|
+/* EP interface number */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_INTF_MASK
|
|
+@brief Mask for EP interface number bits
|
|
+*/
|
|
+#define UDC_CSR_NE_INTF_MASK 0x00007800
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_INTF_OFS
|
|
+@brief Offset for EP interface number bits
|
|
+*/
|
|
+#define UDC_CSR_NE_INTF_OFS 11
|
|
+
|
|
+/* EP alt setting */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_ALT_MASK
|
|
+@brief Mask for EP alt setting
|
|
+*/
|
|
+#define UDC_CSR_NE_ALT_MASK 0x00078000
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_ALT_OFS
|
|
+@brief Offset for EP alt bits
|
|
+*/
|
|
+#define UDC_CSR_NE_ALT_OFS 15
|
|
+
|
|
+/* max pkt */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_MAX_PKT_MASK
|
|
+@brief Mask for max packet bits
|
|
+*/
|
|
+#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_CSR_NE_MAX_PKT_OFS
|
|
+@brief Offset for max packet bits
|
|
+*/
|
|
+#define UDC_CSR_NE_MAX_PKT_OFS 19
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_EP_NUM
|
|
+@brief Total number of EPs
|
|
+*/
|
|
+#define IOH_UDC_EP_NUM 32 /* 16 IN and 16 OUT */
|
|
+/* EP number of EP's really used */
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_USED_EP_NUM
|
|
+@brief number of EPs used
|
|
+*/
|
|
+#define IOH_UDC_USED_EP_NUM 4
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP0IN_IDX
|
|
+@brief Control IN ep index
|
|
+*/
|
|
+#define UDC_EP0IN_IDX 0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_EP0UT_IDX
|
|
+@brief Control OUT ep index
|
|
+*/
|
|
+#define UDC_EP0OUT_IDX 1
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_EP0
|
|
+@brief number of EP0
|
|
+*/
|
|
+#define IOH_UDC_EP0 0
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_EP1
|
|
+@brief number of EP1
|
|
+*/
|
|
+#define IOH_UDC_EP1 1
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_EP2
|
|
+@brief number of EP2
|
|
+*/
|
|
+#define IOH_UDC_EP2 2
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def IOH_UDC_EP3
|
|
+@brief number of EP3
|
|
+*/
|
|
+#define IOH_UDC_EP3 3
|
|
+
|
|
+/* Rx fifo address and size = 2k -------------------------------------------*/
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_RXFIFO_ADDR
|
|
+@brief Address offset of Rx FIFO
|
|
+*/
|
|
+#define UDC_RXFIFO_ADDR 0x800
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_RXFIFO_SIZE
|
|
+@brief Rx FIFO size
|
|
+*/
|
|
+#define UDC_RXFIFO_SIZE 0x800
|
|
+
|
|
+/* Tx fifo address and size = 4k -----------------------------------------*/
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_TXFIFO_ADDR
|
|
+@brief Address offset of Tx FIFO
|
|
+*/
|
|
+#define UDC_TXFIFO_ADDR 0x1000
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_TXFIFO_SIZE
|
|
+@brief Tx FIFO size
|
|
+*/
|
|
+#define UDC_TXFIFO_SIZE 0x1000
|
|
+
|
|
+/* general constants */
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_DWORD_BYTES
|
|
+@brief Bytes in DWORD
|
|
+*/
|
|
+#define UDC_DWORD_BYTES 4
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_BITS_PER_BYTE
|
|
+@brief Bits in a byte
|
|
+*/
|
|
+#define UDC_BITS_PER_BYTE 8
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_BITS_PER_BYTE_SHIFT
|
|
+*/
|
|
+#define UDC_BITS_PER_BYTE_SHIFT 3
|
|
+
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@def UDC_BYTE_MASK
|
|
+@brief Mask to get lower byte
|
|
+*/
|
|
+#define UDC_BYTE_MASK 0xff
|
|
+
|
|
+/* Endpoint configuration regsisters */
|
|
+/*!@ingroup UDC_HALLayer
|
|
+@struct ioh_udc_csrs_
|
|
+@brief Structure to Endpoint configuration registers
|
|
+*/
|
|
+struct ioh_udc_csrs {
|
|
+ u32 ne[IOH_UDC_USED_EP_NUM * 2];
|
|
+};
|
|
+
|
|
+/* UDC_Global registers */
|
|
+/*!@ingroup UDC_Global
|
|
+@struct ioh_udc_regs_
|
|
+@brief Structure holding values of configuration registers
|
|
+*/
|
|
+struct ioh_udc_regs {
|
|
+ u32 devcfg; /**< Device configuration register */
|
|
+ u32 devctl; /**< Device control register */
|
|
+ u32 devsts; /**< Device status register */
|
|
+ u32 devirqsts; /**< Device irq status register */
|
|
+ u32 devirqmsk; /**< Device irq mask register */
|
|
+ u32 epirqsts; /**< Endpoint irq status register */
|
|
+ u32 epirqmsk; /**< Endpoint irq mask register */
|
|
+ u32 devlpm; /**< LPM control/status register */
|
|
+};
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+@struct ioh_udc_ep_regs__
|
|
+@brief Structure holding values of ep configuration registers
|
|
+*/
|
|
+struct ioh_udc_ep_regs {
|
|
+ u32 epctl; /**< Endpoint control register */
|
|
+ u32 epsts; /**< Endpoint status register */
|
|
+ u32 bufin_framenum; /**< buffer size in / frame number out */
|
|
+ u32 bufout_maxpkt; /**< buffer size out / maxpkt in */
|
|
+ u32 subptr; /**< setup buffer pointer */
|
|
+ u32 desptr; /**< Data descriptor pointer */
|
|
+ u32 confirm; /**< Write/Read confirmation for slave mode only */
|
|
+};
|
|
+
|
|
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+@struct ioh_udc_data_dma_desc_
|
|
+@brief Structure to hold DMA descriptor information for data
|
|
+*/
|
|
+struct ioh_udc_data_dma_desc {
|
|
+ u32 status; /**< status quadlet */
|
|
+ u32 reserved;
|
|
+ u32 dataptr; /**< buffer descriptor */
|
|
+ u32 next; /**< next descriptor */
|
|
+};
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+@struct struct ioh_udc_stp_dma_desc_
|
|
+@brief Structure to hold DMA descriptor information for control data
|
|
+*/
|
|
+struct ioh_udc_stp_dma_desc {
|
|
+ u32 status;
|
|
+ u32 reserved;
|
|
+ u32 data12; /**< first setup word */
|
|
+ u32 data34; /**< second setup word */
|
|
+};
|
|
+
|
|
+/*!@ingroup UDC_Global
|
|
+@struct ioh_udc_cfg_data
|
|
+@brief Structure to hold current configuration and interface information
|
|
+*/
|
|
+struct ioh_udc_cfg_data {
|
|
+ u16 cur_cfg; /**< current configuration in use */
|
|
+ u16 cur_intf; /**< current interface in use */
|
|
+ u16 cur_alt; /**< current alt interface in use */
|
|
+};
|
|
+
|
|
+/* DMA status definitions */
|
|
+#define IOH_UDC_BUFF_STS 0xC0000000 /**< Buffer status mask */
|
|
+#define IOH_UDC_BS_HST_RDY 0x00000000 /**< 2'b00 : Host Ready */
|
|
+#define IOH_UDC_BS_DMA_BSY 0x40000000 /**< 2'b01 : DMA Busy */
|
|
+#define IOH_UDC_BS_DMA_DONE 0x80000000 /**< 2'b10 : DMA Done */
|
|
+#define IOH_UDC_BS_HST_BSY 0xC0000000 /**< 2'b11 : HOST busy */
|
|
+
|
|
+#define IOH_UDC_RXTX_STS 0x30000000 /**< Rx/Tx Status Mask */
|
|
+#define IOH_UDC_RTS_SUCC 0x00000000 /**< Success */
|
|
+#define IOH_UDC_RTS_DESERR 0x10000000 /**< Descriptor Error */
|
|
+#define IOH_UDC_RTS_BUFERR 0x30000000 /**< Buffer Error */
|
|
+
|
|
+#define IOH_UDC_DMA_LAST 0x08000000 /**< Last Descriptor Indication */
|
|
+#define IOH_UDC_RXTX_BYTES 0x0000ffff /**< Number of Rx/Tx Bytes Mask */
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_print_regs(u32 base)
|
|
+@brief Prints UDC device registers and endpoint irq registers
|
|
+*/
|
|
+extern void ioh_udc_print_regs(u32 base);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_init(struct ioh_udc_regs *dev)
|
|
+@brief Initializes the UDC hardware
|
|
+*/
|
|
+extern void ioh_udc_init(struct ioh_udc_regs *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_exit(struct ioh_udc_regs *dev)
|
|
+@brief This API will do any cleanup required for the USB device hardware.
|
|
+*/
|
|
+extern void ioh_udc_exit(struct ioh_udc_regs *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_write_csr(unsigned long val, unsigned long addr)
|
|
+@brief Write to CSR register
|
|
+*/
|
|
+extern void ioh_udc_write_csr(unsigned long val, unsigned long addr);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_read_csr(unsigned long addr)
|
|
+@brief Read the content of CSR
|
|
+*/
|
|
+extern u32 ioh_udc_read_csr(unsigned long addr);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_enable(struct ioh_udc_ep_regs __iomem *regs,
|
|
+ struct ioh_udc_cfg_data *cfg, struct usb_endpoint_descriptor *desc);
|
|
+@brief Enables endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_enable(struct ioh_udc_ep_regs __iomem *regs,
|
|
+ struct ioh_udc_cfg_data *cfg,
|
|
+ const struct usb_endpoint_descriptor *desc);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_disable(struct ioh_udc_ep_regs __iomem *regs)
|
|
+@brief Disables endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_disable(struct ioh_udc_ep_regs __iomem *regs);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_get_frame(struct ioh_udc_regs __iomem *dev)
|
|
+@brief This API will return the current frame number
|
|
+*/
|
|
+extern int ioh_udc_get_frame(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_set_dma(struct ioh_udc_regs *dev, int dir)
|
|
+@brief Enables Tx/Rx DMA
|
|
+*/
|
|
+extern void ioh_udc_set_dma(struct ioh_udc_regs *dev, int dir);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_clear_dma(struct ioh_udc_regs *dev, int dir)
|
|
+@brief Disable Tx/Rx DMA
|
|
+*/
|
|
+extern void ioh_udc_clear_dma(struct ioh_udc_regs *dev, int dir);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_rmt_wakeup(struct ioh_udc_regs __iomem *dev)
|
|
+@brief Initiates a remote wakeup
|
|
+*/
|
|
+extern void ioh_udc_rmt_wakeup(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_clear_selfpowered (struct ioh_udc_regs __iomem *dev)
|
|
+@brief This API will clear the self powered feature of the device
|
|
+*/
|
|
+extern void ioh_udc_clear_selfpowered(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_set_selfpowered (struct ioh_udc_regs __iomem *dev)
|
|
+@brief This API will set the self powered feature of the device
|
|
+*/
|
|
+extern void ioh_udc_set_selfpowered(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_set_disconnect (struct ioh_udc_regs __iomem *dev)
|
|
+@brief This API will cause the device to enter soft disconnect state
|
|
+*/
|
|
+extern void ioh_udc_set_disconnect(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_clear_disconnect (struct ioh_udc_regs __iomem *dev)
|
|
+@brief This API will get the device out of soft disconnect state.
|
|
+*/
|
|
+extern void ioh_udc_clear_disconnect(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_vbus_session (struct ioh_udc_regs __iomem *dev, int is_active)
|
|
+@brief This API will be called when VBUS power is made active for the device.
|
|
+*/
|
|
+extern void ioh_udc_vbus_session(struct ioh_udc_regs __iomem *dev,
|
|
+ int is_active);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_clear_stall(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief This API will clear the STALL handshake feature of the specified endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_clear_stall(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_stall(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief This API will set the STALL handshake feature of the specified endpoint.
|
|
+*/
|
|
+extern void ioh_udc_ep_set_stall(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_clear_nak(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief This API will stop the endpoint from issuing NAK packets.
|
|
+*/
|
|
+extern void ioh_udc_ep_clear_nak(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_nak(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief This API will cause the endpoint to issue NAK packets.
|
|
+*/
|
|
+extern void ioh_udc_ep_set_nak(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_fifo_flush(struct ioh_udc_ep_regs __iomem *ep, int dir)
|
|
+@brief This API will flush the FIFO of the specified endpoint.
|
|
+*/
|
|
+extern void ioh_udc_ep_fifo_flush(struct ioh_udc_ep_regs __iomem *ep, int dir);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_disable_interrupts(struct ioh_udc_regs *dev, u32 mask)
|
|
+@brief This API will disable the specified device interrupts
|
|
+*/
|
|
+extern void ioh_udc_disable_interrupts(struct ioh_udc_regs *dev, u32 mask);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_enable_interrupts(struct ioh_udc_regs *dev, u32 mask)
|
|
+@brief This API will enable specified device interrupts
|
|
+*/
|
|
+extern void ioh_udc_enable_interrupts(struct ioh_udc_regs *dev, u32 mask);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_disable_ep_interrupts(struct ioh_udc_regs *dev, u32 mask)
|
|
+@brief This API will disable the interrupts from specific endpoint.
|
|
+*/
|
|
+extern void ioh_udc_disable_ep_interrupts(struct ioh_udc_regs *dev, u32 mask);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_enable_ep_interrupts(struct ioh_udc_regs *dev, u32 mask)
|
|
+@brief This API will enable the interrupts from specific endpoint.
|
|
+*/
|
|
+extern void ioh_udc_enable_ep_interrupts(struct ioh_udc_regs *dev, u32 mask);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_set_csr_done(struct ioh_udc_regs *dev)
|
|
+@brief This API will inform the device the completion of
|
|
+ USB device programming.
|
|
+*/
|
|
+extern void ioh_udc_set_csr_done(struct ioh_udc_regs *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_set_burst_length(struct ioh_udc_regs *dev, u8 len)
|
|
+@brief This API will set the length of 32bit words on a
|
|
+ single burst of DMA .
|
|
+*/
|
|
+extern void ioh_udc_set_burst_length(struct ioh_udc_regs *dev, u8 len);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_set_threshold_length(struct ioh_udc_regs *dev, u8 len)
|
|
+@brief This API will set the length of 32bit words after
|
|
+ which DMA can start
|
|
+*/
|
|
+extern void ioh_udc_set_threshold_length(struct ioh_udc_regs *dev, u8 len);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_get_speed (struct ioh_udc_regs __iomem *dev
|
|
+@brief This API will return the current speed
|
|
+*/
|
|
+extern int ioh_udc_get_speed(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_trfr_type(struct ioh_udc_ep_regs __iomem *ep, u8 type)
|
|
+@brief This API will set the endpoint type in the endpoint control register.
|
|
+*/
|
|
+extern void ioh_udc_ep_set_trfr_type(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u8 type);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_maxpkt(struct ioh_udc_ep_regs __iomem *ep, u32 pkt_size)
|
|
+@brief This API will set the maximum packet size for the endpoint.
|
|
+*/
|
|
+extern void ioh_udc_ep_set_maxpkt(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 pkt_size);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_bufsz(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 buf_size, u32 ep_in)
|
|
+@brief Sets buffer size for the endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_set_bufsz(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 buf_size, u32 ep_in);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_ddptr(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 addr)
|
|
+@brief This API will set the data descriptor pointer for the endpoint.
|
|
+*/
|
|
+extern void ioh_udc_ep_set_ddptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_subptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr)
|
|
+@brief Set the Setup buffer pointer for the endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_set_subptr(struct ioh_udc_ep_regs __iomem *ep, u32 addr);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_read_device_interrupts(struct ioh_udc_regs __iomem *dev)
|
|
+@brief Returns the interrupt status
|
|
+*/
|
|
+extern u32 ioh_udc_read_device_interrupts(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_write_device_interrupts(struct ioh_udc_regs __iomem *dev, u32 val);
|
|
+@brief Writes Interrupts
|
|
+*/
|
|
+extern void ioh_udc_write_device_interrupts(struct ioh_udc_regs __iomem *dev,
|
|
+ u32 val);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_read_ep_interrupts(struct ioh_udc_regs __iomem *ep)
|
|
+@brief Reads endpoint interrupts
|
|
+*/
|
|
+extern u32 ioh_udc_read_ep_interrupts(struct ioh_udc_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_write_ep_interrupts(struct ioh_udc_regs __iomem *ep, u32 val)
|
|
+@brief Writes endpoint interrupts
|
|
+*/
|
|
+extern void ioh_udc_write_ep_interrupts(struct ioh_udc_regs __iomem *ep,
|
|
+ u32 val);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_read_device_status(struct ioh_udc_regs __iomem *dev)
|
|
+@brief Reads device status register
|
|
+*/
|
|
+extern u32 ioh_udc_read_device_status(struct ioh_udc_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_read_ep_control(struct ioh_udc_ep_regs __iomem *dev)
|
|
+@brief Reads the endpoint status registers.
|
|
+*/
|
|
+extern u32 ioh_udc_read_ep_control(struct ioh_udc_ep_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_clear_ep_control(struct ioh_udc_ep_regs __iomem *dev)
|
|
+@brief Clear the endpoint status registers.
|
|
+*/
|
|
+extern void ioh_udc_clear_ep_control(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_read_ep_status(struct ioh_udc_ep_regs __iomem *dev)
|
|
+@brief Reads the endpoint status registers.
|
|
+*/
|
|
+extern u32 ioh_udc_read_ep_status(struct ioh_udc_ep_regs __iomem *dev);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_pd(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief Set the poll demand bit for the endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_set_pd(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_set_rrdy(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief Set the receive ready bit for the endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_set_rrdy(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_ep_clear_rrdy(struct ioh_udc_ep_regs __iomem *ep)
|
|
+@brief Clear the receive ready bit for the endpoint
|
|
+*/
|
|
+extern void ioh_udc_ep_clear_rrdy(struct ioh_udc_ep_regs __iomem *ep);
|
|
+
|
|
+/*!@ingroup UDC_HALLayerAPI
|
|
+@fn ioh_udc_clear_ep_status(struct ioh_udc_ep_regs __iomem *ep, u32 stat)
|
|
+@brief Clears endpoint status register
|
|
+*/
|
|
+extern void ioh_udc_clear_ep_status(struct ioh_udc_ep_regs __iomem *ep,
|
|
+ u32 stat);
|
|
+extern int speed_fs;
|
|
+#endif /* IOH_UDC_HAL_H_ */
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc_intr.c
|
|
@@ -0,0 +1,396 @@
|
|
+ /*!
|
|
+ * @file ioh_udc_intr.c
|
|
+ * @brief
|
|
+ * The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ * It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or
|
|
+ * interrupt type).
|
|
+ *
|
|
+ * The IOH USB device controller driver provides required interface
|
|
+ * to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ * @version 0.96
|
|
+ *
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * History:
|
|
+ * Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ * modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/usb/ch9.h>
|
|
+#include <linux/usb/gadget.h>
|
|
+/*#include <asm/io.h>*/
|
|
+#include <linux/io.h>
|
|
+#include "pch_common.h"
|
|
+#include "pch_debug.h"
|
|
+
|
|
+#include "pch_udc_hal.h"
|
|
+#include "pch_udc.h"
|
|
+
|
|
+/* received setup data */
|
|
+static union ioh_udc_setup_data setup_data;
|
|
+
|
|
+/*! @ingroup UDC_UtilitiesAPI
|
|
+ * @fn void ioh_udc_svc_ur_interrupt(struct ioh_udc_dev *dev)
|
|
+ * @brief This function handles a USB reset interrupt
|
|
+ * @param dev [@ref IN] reference to driver structure
|
|
+ * @return none
|
|
+ * @remarks The following actions are performed:
|
|
+ *- Disable Rx/TX DMA using ioh_udc_clear_dma API
|
|
+ *- Mask all endpoint interrupts by invoking ioh_udc_disable_ep_interrupts API
|
|
+ *- Clear all endpoint interrupts by invoking ioh_udc_write_ep_interrupts API
|
|
+ *- Clear all endpoint status register invoking ioh_udc_clear_ep_status API
|
|
+ *- Clear all endpoint control register by invoking ioh_udc_clear_ep_control API
|
|
+ *- Clear all endpoint Data descriptor pointer register by invoking
|
|
+ * ioh_udc_ep_set_ddptr API
|
|
+ *- Clear all endpoint UDC Endpoint register by invoking ioh_udc_write_csr API
|
|
+ *- Clear status flags
|
|
+ *- Set the all endpoint's NAK by invoking ioh_udc_ep_set_nak API
|
|
+ *- Flush the all endpoint's Tx/RxFIFO by invoking ioh_udc_ep_fifo_flush API
|
|
+ *- Disable ep0 to empty request queue by invoking empty_req_queue API
|
|
+ *- Invoke the gadget driver's fs disconnect method
|
|
+- Empty EP0 IN request queue using empty_req_queue API
|
|
+ * @see
|
|
+ * - ioh_udc_clear_dma
|
|
+ * - ioh_udc_disable_ep_interrupts
|
|
+ * - ioh_udc_write_ep_interrupts
|
|
+ * - ioh_udc_clear_ep_status
|
|
+ * - ioh_udc_clear_ep_control
|
|
+ * - ioh_udc_ep_set_ddptr
|
|
+ * - ioh_udc_write_csr
|
|
+ * - ioh_udc_ep_set_nak
|
|
+ * - ioh_udc_ep_fifo_flush
|
|
+ * - empty_req_queue
|
|
+ */
|
|
+void ioh_udc_svc_ur_interrupt(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ struct ioh_udc_ep *ep;
|
|
+ int i;
|
|
+
|
|
+ IOH_DEBUG("USB_RESET Enter");
|
|
+
|
|
+#ifdef IOH_PRINT_REG
|
|
+ ioh_udc_print_regs((u32)dev->virt_addr);
|
|
+#endif
|
|
+
|
|
+ /* Disable DMA */
|
|
+ ioh_udc_clear_dma(dev->regs, DMA_DIR_TX);
|
|
+ ioh_udc_clear_dma(dev->regs, DMA_DIR_RX);
|
|
+ /* Mask all endpoint interrupts */
|
|
+ ioh_udc_disable_ep_interrupts(dev->regs, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+ /* clear all endpoint interrupts */
|
|
+ ioh_udc_write_ep_interrupts(dev->regs, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+
|
|
+ for (i = 0; i < IOH_UDC_EP_NUM; i++) {
|
|
+ ep = &dev->ep[i];
|
|
+ ioh_udc_clear_ep_status(ep->regs, 0x1F0006F0);
|
|
+ ioh_udc_clear_ep_control(ep->regs);
|
|
+ ioh_udc_ep_set_ddptr(ep->regs, 0);
|
|
+ ioh_udc_write_csr(0x00, (u32) (&dev->csr->ne[i]));
|
|
+ }
|
|
+
|
|
+ dev->stall = 0;
|
|
+ dev->prot_stall = 0;
|
|
+ dev->waiting_zlp_ack = 0;
|
|
+ dev->set_cfg_not_acked = 0;
|
|
+
|
|
+ /* disable ep to empty req queue. Skip the control EP's */
|
|
+ for (i = 0; i < (IOH_UDC_USED_EP_NUM*2); i++) {
|
|
+ ep = &dev->ep[i];
|
|
+ /* Set NAK */
|
|
+ ioh_udc_ep_set_nak(ep->regs);
|
|
+ /* Flush fifo */
|
|
+ ioh_udc_ep_fifo_flush(ep->regs , ep->in);
|
|
+ /* Complete request queue */
|
|
+ empty_req_queue(ep);
|
|
+ }
|
|
+ if (dev->driver && dev->driver->disconnect)
|
|
+ dev->driver->disconnect(&dev->gadget);
|
|
+
|
|
+ IOH_DEBUG("USB_RESET Exit");
|
|
+}
|
|
+
|
|
+/*! @ingroup UDC_UtilitiesAPI
|
|
+ * @fn ioh_udc_svc_enum_interrupt(struct ioh_udc_dev *dev)
|
|
+ * @brief This function handles a USB speed enumeration done interrupt
|
|
+ * @param dev [@ref IN] reference to driver structure
|
|
+ * @return none
|
|
+ * @remarks The following actions are performed:
|
|
+ * - Read the device status using ioh_udc_read_device_status API
|
|
+ * - Set the speed element in device structure.
|
|
+ * - Activate control endpoint using ioh_udc_activate_control_ep API
|
|
+ * - Enable EP0 interrupts using ioh_udc_enable_ep_interrupts API
|
|
+ * - Enable Rx/TX DMA using ioh_udc_set_dma API
|
|
+ * - Set receive ready using ioh_udc_ep_set_rrdy API
|
|
+ * @see
|
|
+ * - ioh_udc_read_device_status
|
|
+ * - ioh_udc_activate_control_ep
|
|
+ * - ioh_udc_enable_ep_interrupts
|
|
+ * - ioh_udc_set_dma
|
|
+ * - ioh_udc_ep_set_rrdy
|
|
+ */
|
|
+void
|
|
+ioh_udc_svc_enum_interrupt(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ u32 dev_stat, dev_speed;
|
|
+ u32 speed = USB_SPEED_FULL;
|
|
+
|
|
+ dev_stat = ioh_udc_read_device_status(dev->regs);
|
|
+ dev_speed = (dev_stat & UDC_DEVSTS_ENUM_SPEED_MASK) >>
|
|
+ UDC_DEVSTS_ENUM_SPEED_OFS;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_svc_enum_interrupt: dev_speed = 0x%08x", dev_speed);
|
|
+
|
|
+ if (dev_speed == UDC_DEVSTS_ENUM_SPEED_HIGH) {
|
|
+ IOH_DEBUG("HighSpeed");
|
|
+ speed = USB_SPEED_HIGH;
|
|
+ } else if (dev_speed == UDC_DEVSTS_ENUM_SPEED_FULL) {
|
|
+ IOH_DEBUG("FullSpeed");
|
|
+ speed = USB_SPEED_FULL;
|
|
+ } else if (dev_speed == UDC_DEVSTS_ENUM_SPEED_LOW) {
|
|
+ IOH_DEBUG("LowSpeed?");
|
|
+ speed = USB_SPEED_LOW;
|
|
+ } else {
|
|
+ IOH_DEBUG("FullSpeed?");
|
|
+ }
|
|
+ dev->gadget.speed = speed;
|
|
+
|
|
+ ioh_udc_activate_control_ep(dev);
|
|
+
|
|
+ /* enable ep0 interrupts */
|
|
+ ioh_udc_enable_ep_interrupts(dev->regs, 1 << UDC_EPINT_IN_EP0 |
|
|
+ 1 << UDC_EPINT_OUT_EP0);
|
|
+
|
|
+ /* enable DMA */
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_TX);
|
|
+ ioh_udc_set_dma(dev->regs, DMA_DIR_RX);
|
|
+ ioh_udc_ep_set_rrdy(dev->ep[UDC_EP0OUT_IDX].regs);
|
|
+
|
|
+#ifdef IOH_PRINT_REG
|
|
+ ioh_udc_print_regs((u32)dev->virt_addr);
|
|
+#endif
|
|
+
|
|
+ IOH_DEBUG("EP mask set to %x",
|
|
+ IOH_READ32((u32 *)&(dev->regs->epirqmsk)));
|
|
+ IOH_DEBUG("USB_SPEED_ENUM Exit");
|
|
+}
|
|
+
|
|
+/*! @ingroup UDC_UtilitiesAPI
|
|
+ * @fn ioh_udc_svc_intf_interrupt(struct ioh_udc_dev *dev)
|
|
+ * @brief This function handles a set interface interrupt
|
|
+ * @param dev [@ref IN] reference to driver structure
|
|
+ * @return none
|
|
+ * @remarks The following actions are performed:
|
|
+ * - Read the device status using ioh_udc_read_device_status API
|
|
+ * - Update current interface in use and current alternate interface
|
|
+ * in use status from the device status information
|
|
+ * - Set device "Set Config Not ACKed" status as TRUE
|
|
+ * - Construct the usb request for gadget driver and inform it
|
|
+ * - Program the Endpoint configuration registers using ioh_udc_read_csr
|
|
+ * and ioh_udc_write_csr APIs
|
|
+ * - Clear stall bit status using ioh_udc_ep_clear_stall
|
|
+ * - Call gadget with setup data received
|
|
+ * @see
|
|
+ * - ioh_udc_read_device_status
|
|
+ * - ioh_udc_read_csr
|
|
+ * - ioh_udc_write_csr
|
|
+ * - ioh_udc_ep_clear_stall
|
|
+ */
|
|
+void
|
|
+ioh_udc_svc_intf_interrupt(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ u32 reg, dev_stat = 0;
|
|
+ int i, ret;
|
|
+
|
|
+ IOH_DEBUG("SI");
|
|
+ dev_stat = ioh_udc_read_device_status(dev->regs);
|
|
+ dev->cfg_data.cur_intf = (dev_stat & UDC_DEVSTS_INTF_MASK) >>
|
|
+ UDC_DEVSTS_INTF_OFS;
|
|
+ dev->cfg_data.cur_alt = (dev_stat & UDC_DEVSTS_ALT_MASK) >>
|
|
+ UDC_DEVSTS_ALT_OFS;
|
|
+ IOH_DEBUG("DVSTATUS=%08x, cfg=%d, intf=%d, alt=%d", dev_stat,
|
|
+ (dev_stat & UDC_CSR_NE_CFG_MASK) >> UDC_CSR_NE_CFG_OFS,
|
|
+ dev->cfg_data.cur_intf, dev->cfg_data.cur_alt);
|
|
+
|
|
+ dev->set_cfg_not_acked = 1;
|
|
+
|
|
+ /* Construct the usb request for gadget driver and inform it */
|
|
+ memset(&setup_data, 0 , sizeof 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->cfg_data.cur_alt);
|
|
+ setup_data.request.wIndex = cpu_to_le16(dev->cfg_data.cur_intf);
|
|
+
|
|
+ /* programm the Endpoint Cfg registers */
|
|
+ for (i = 0; i < IOH_UDC_USED_EP_NUM * 2; i++) {
|
|
+ if (i == 1) { /* Only one end point cfg register */
|
|
+ reg = ioh_udc_read_csr((u32) (&dev->csr->ne[i]));
|
|
+ reg = (reg & ~UDC_CSR_NE_INTF_MASK) |
|
|
+ (dev->cfg_data.cur_intf << UDC_CSR_NE_INTF_OFS);
|
|
+ reg = (reg & ~UDC_CSR_NE_ALT_MASK) |
|
|
+ (dev->cfg_data.cur_alt << UDC_CSR_NE_ALT_OFS);
|
|
+ ioh_udc_write_csr(reg, (u32) (&dev->csr->ne[i]));
|
|
+ }
|
|
+ /* clear stall bits */
|
|
+ ioh_udc_ep_clear_stall(dev->ep[i].regs);
|
|
+ dev->ep[i].halted = 0;
|
|
+ }
|
|
+ dev->stall = 0;
|
|
+ spin_unlock(&dev->lock);
|
|
+ ret = dev->driver->setup(&dev->gadget, &setup_data.request);
|
|
+ spin_lock(&dev->lock);
|
|
+
|
|
+ IOH_DEBUG("EXIT -- SI");
|
|
+}
|
|
+
|
|
+/*! @ingroup UDC_UtilitiesAPI
|
|
+ * @fn ioh_udc_svc_cfg_interrupt(struct ioh_udc_dev *dev)
|
|
+ * @brief This function handles a set configuration interrupt
|
|
+ * @param dev [@ref IN] reference to driver structure
|
|
+ * @return none
|
|
+ * @remarks The following actions are performed:
|
|
+ * - Read the device status using ioh_udc_read_device_status API
|
|
+ * - Set the set_cfg_not_acked element in device structure to 1.
|
|
+ * - Update "current configuration in use" status based on the
|
|
+ * device status
|
|
+ * - Construct a usb request for gadget driver
|
|
+ * - Program the Endpoint configuration registers using
|
|
+ * ioh_udc_read_csr and ioh_udc_write_csr APIs
|
|
+ * - Clear stall bit status using ioh_udc_ep_clear_stall
|
|
+ * - Call gadget with setup data received
|
|
+ * @see
|
|
+ * - ioh_udc_read_device_status
|
|
+ * - ioh_udc_read_csr
|
|
+ * - ioh_udc_write_csr
|
|
+ * - ioh_udc_ep_clear_stall
|
|
+ */
|
|
+void
|
|
+ioh_udc_svc_cfg_interrupt(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ int i, ret;
|
|
+ u32 reg, dev_stat = 0;
|
|
+
|
|
+ IOH_DEBUG("SC");
|
|
+ dev_stat = ioh_udc_read_device_status(dev->regs);
|
|
+ IOH_DEBUG("DVSTATUS=%08x, cfg=%d, intf=%d, alt=%d", dev_stat,
|
|
+ (dev_stat & UDC_DEVSTS_CFG_MASK) >> UDC_DEVSTS_CFG_OFS,
|
|
+ (dev_stat & UDC_DEVSTS_INTF_MASK) >> UDC_DEVSTS_INTF_OFS,
|
|
+ (dev_stat & UDC_DEVSTS_ALT_MASK) >> UDC_DEVSTS_ALT_OFS);
|
|
+
|
|
+ dev->set_cfg_not_acked = 1;
|
|
+
|
|
+ dev->cfg_data.cur_cfg = (dev_stat & UDC_DEVSTS_CFG_MASK) >>
|
|
+ UDC_DEVSTS_CFG_OFS;
|
|
+ /* make usb request for gadget driver */
|
|
+ memset(&setup_data, 0 , sizeof setup_data);
|
|
+ setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
|
|
+ setup_data.request.wValue = cpu_to_le16(dev->cfg_data.cur_cfg);
|
|
+
|
|
+ /* program the NE registers */
|
|
+ for (i = 0; i < IOH_UDC_USED_EP_NUM * 2; i++) {
|
|
+ if (i == 1) {
|
|
+ reg = ioh_udc_read_csr((u32) (&dev->csr->ne[i]));
|
|
+ reg = (reg & ~UDC_CSR_NE_CFG_MASK) |
|
|
+ (dev->cfg_data.cur_cfg << UDC_CSR_NE_CFG_OFS);
|
|
+ ioh_udc_write_csr(reg, (u32) (&dev->csr->ne[i]));
|
|
+ }
|
|
+ /* clear stall bits */
|
|
+ ioh_udc_ep_clear_stall(dev->ep[i].regs);
|
|
+ dev->ep[i].halted = 0;
|
|
+ }
|
|
+ dev->stall = 0;
|
|
+
|
|
+ /* call gadget zero with setup data received */
|
|
+ spin_unlock(&dev->lock);
|
|
+ ret = dev->driver->setup(&dev->gadget, &setup_data.request);
|
|
+ spin_lock(&dev->lock);
|
|
+
|
|
+ IOH_DEBUG("SC Exit...ret %d", ret);
|
|
+}
|
|
+
|
|
+/*! @ingroup UDC_UtilitiesAPI
|
|
+ * @fn void ioh_udc_dev_isr(struct ioh_udc_dev *dev, u32 dev_intr)
|
|
+ * @brief This function services device interrupts by invoking appropriate
|
|
+ * routines.
|
|
+ * @remarks The following actions are performed:
|
|
+ *- If USB reset interrupt status is received, invoke ioh_udc_svc_ur_interrupt
|
|
+ * function
|
|
+ *- If Enumeration done interrupt is received, invoke ioh_udc_svc_enum_interrupt
|
|
+ * function
|
|
+ *- If Set Interface interrupt is received, invoke ioh_udc_svc_intf_interrupt
|
|
+ * function
|
|
+ *- If Set Config interrupt is received, invoke ioh_udc_svc_cfg_interrupt
|
|
+ * function
|
|
+ *- If USB suspend interrupt or ES interrupt is received, invoke the
|
|
+ * API ioh_udc_rmt_wakeup
|
|
+ *- For the following interrupts, log a message in the system log:
|
|
+ * - USB Suspend interrupt
|
|
+ * - SOF token detection interrupt
|
|
+ * - ES interrupt, (IDLE > 3ms on the USB)
|
|
+ * - RWKP interrupt (Remote Wakeup)
|
|
+ * @param dev Reference to the device structure
|
|
+ * @param dev_intr The Device interrupt status.
|
|
+ * @see
|
|
+ * - ioh_udc_svc_ur_interrupt
|
|
+ * - ioh_udc_svc_enum_interrupt
|
|
+ * - ioh_udc_svc_intf_interrupt
|
|
+ * - ioh_udc_svc_cfg_interrupt
|
|
+ * @return none
|
|
+ */
|
|
+void ioh_udc_dev_isr(struct ioh_udc_dev *dev, u32 dev_intr)
|
|
+{
|
|
+ /* USB Reset Interrupt */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_UR))
|
|
+ ioh_udc_svc_ur_interrupt(dev);
|
|
+
|
|
+ /* Enumeration Done Interrupt */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_ENUM))
|
|
+ ioh_udc_svc_enum_interrupt(dev);
|
|
+
|
|
+ /* Set Interface Interrupt */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_SI))
|
|
+ ioh_udc_svc_intf_interrupt(dev);
|
|
+
|
|
+ /* Set Config Interrupt */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_SC))
|
|
+ ioh_udc_svc_cfg_interrupt(dev);
|
|
+
|
|
+ /* USB Suspend interrupt */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_US))
|
|
+ IOH_DEBUG("USB_SUSPEND");
|
|
+
|
|
+ /* Clear the SOF interrupt, if enabled */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_SOF))
|
|
+ IOH_DEBUG("SOF");
|
|
+
|
|
+ /* ES interrupt, IDLE > 3ms on the USB */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_ES))
|
|
+ IOH_DEBUG("ES");
|
|
+
|
|
+ /* RWKP interrupt */
|
|
+ if (dev_intr & (1 << UDC_DEVINT_RWKP))
|
|
+ IOH_DEBUG("RWKP");
|
|
+
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc_pci.c
|
|
@@ -0,0 +1,549 @@
|
|
+/*!
|
|
+ * @file ioh_udc_pci.c
|
|
+ * @brief
|
|
+ * The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ * It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or
|
|
+ * interrupt type).
|
|
+ *
|
|
+ * The IOH USB device controller driver provides required interface
|
|
+ * to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ * @version 0.96
|
|
+ *
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * History:
|
|
+ * Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ * modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * ioh_udc_pci.c -- IOH UDC high/full speed USB device controller
|
|
+ *
|
|
+ */
|
|
+
|
|
+
|
|
+/*!@ingroup UDC_PCILayer
|
|
+ * @def UDC_MOD_DESCRIPTION
|
|
+ * @brief USB device controller driver description.
|
|
+ */
|
|
+#define UDC_MOD_DESCRIPTION "OKISEMI IOH UDC - USB Device Controller"
|
|
+
|
|
+/*!@ingroup UDC_PCILayer
|
|
+ * @def IOH_UDC_PCI_BAR
|
|
+ * @brief Number of PCI BAR.
|
|
+ */
|
|
+#define IOH_UDC_PCI_BAR 1
|
|
+
|
|
+/* udc specific */
|
|
+#include "pch_common.h"
|
|
+#include "pch_debug.h"
|
|
+#include "pch_udc_pci.h"
|
|
+#include "pch_udc.h"
|
|
+
|
|
+static int ioh_udc_probe(struct pci_dev *pdev, const struct pci_device_id *id);
|
|
+static void ioh_udc_remove(struct pci_dev *pdev);
|
|
+static void ioh_udc_shutdown(struct pci_dev *pdev);
|
|
+static int ioh_udc_suspend(struct pci_dev *pdev, pm_message_t state);
|
|
+static int ioh_udc_resume(struct pci_dev *pdev);
|
|
+
|
|
+/* description */
|
|
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
|
|
+static const char name[] = "ioh_udc";
|
|
+
|
|
+/* pointer to device object */
|
|
+/*!@ingroup UDC_Global
|
|
+ * @brief pointer to device object
|
|
+ */
|
|
+struct ioh_udc_dev *ioh_udc;
|
|
+
|
|
+/* Speed selection flag */
|
|
+/*!@ingroup UDC_Global
|
|
+ * @brief Specifies operation speed (High or FULL) - passed as module parameter
|
|
+ */
|
|
+int speed_fs;
|
|
+
|
|
+/* module parameters */
|
|
+module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
|
|
+
|
|
+/* Tears down device */
|
|
+/*!@ingroup UDC_PCILayerAPI
|
|
+ * @fn static void gadget_release(struct device *pdev)
|
|
+ * @brief Free the gadget driver private data
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Retrieve the pointer to the private data using dev_get_drvdata API.
|
|
+ * - Free the memory allocated for the device structure using kfree API.
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @return none
|
|
+ */
|
|
+static void gadget_release(struct device *pdev)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = dev_get_drvdata(pdev);
|
|
+ kfree(dev);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_PCILayerAPI
|
|
+ * @fn static void ioh_udc_remove(struct pci_dev *pdev)
|
|
+ * @brief Implements the remove routine for IOH USB device controller driver
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Deallocate any PCI memory pool created
|
|
+ * - Release IRQ
|
|
+ * - Unmap device memory
|
|
+ * - Disable the PCI device
|
|
+ * - Unregister the device from system
|
|
+ * @return none
|
|
+ */
|
|
+static void ioh_udc_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_remove enter");
|
|
+ /* gadget driver must not be registered */
|
|
+ if (dev->driver != NULL)
|
|
+ IOH_LOG(KERN_ERR, "udc_pci_remove: gadget driver\
|
|
+ still bound!!!");
|
|
+
|
|
+ /* dma pool cleanup */
|
|
+ if (dev->data_requests != NULL)
|
|
+ pci_pool_destroy(dev->data_requests);
|
|
+
|
|
+
|
|
+ if (dev->stp_requests != NULL) {
|
|
+ /* cleanup DMA desc's for ep0in */
|
|
+ if (dev->ep[UDC_EP0OUT_IDX].td_stp != NULL) {
|
|
+ pci_pool_free(dev->stp_requests,
|
|
+ dev->ep[UDC_EP0OUT_IDX].td_stp,
|
|
+ dev->ep[UDC_EP0OUT_IDX].td_stp_phys);
|
|
+ }
|
|
+ if (dev->ep[UDC_EP0OUT_IDX].td_data != NULL) {
|
|
+ pci_pool_free(dev->stp_requests,
|
|
+ dev->ep[UDC_EP0OUT_IDX].td_data,
|
|
+ dev->ep[UDC_EP0OUT_IDX].td_data_phys);
|
|
+ }
|
|
+ pci_pool_destroy(dev->stp_requests);
|
|
+ }
|
|
+
|
|
+ ioh_udc_exit(dev->regs);
|
|
+
|
|
+ if (dev->irq_registered)
|
|
+ free_irq(pdev->irq, dev);
|
|
+
|
|
+ if (dev->virt_addr != NULL)
|
|
+ iounmap(dev->virt_addr);
|
|
+
|
|
+ if (dev->mem_region)
|
|
+ release_mem_region(dev->phys_addr, pci_resource_len(pdev,
|
|
+ IOH_UDC_PCI_BAR));
|
|
+
|
|
+ if (dev->active)
|
|
+ pci_disable_device(pdev);
|
|
+
|
|
+ if (dev->registered)
|
|
+ device_unregister(&dev->gadget.dev);
|
|
+ else
|
|
+ kfree(dev);
|
|
+
|
|
+ pci_set_drvdata(pdev, NULL);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_UtilitiesAPI
|
|
+ * @fn static int init_dma_pools(struct ioh_udc_dev *dev)
|
|
+ * @brief create dma pools during initialization
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @remarks The following actions are performed:
|
|
+ *- Create a PCI memory pool of DMA descriptors for handling data requests
|
|
+ * using pci_pool_create API
|
|
+ *- If failed to create the pool, return -ENOMEM
|
|
+ *- Create a PCI memory pool of DMA descriptors for handling setup requests
|
|
+ * using pci_pool_create API
|
|
+ *- If failed to create the pool, return -ENOMEM
|
|
+ *- Allocate one entry from setup pool to be used for setup requests using
|
|
+ * pci_pool_alloc API
|
|
+ *- If failed to allocate, return -ENOMEM
|
|
+ *- Allocate one entry from setup pool to be used for control IN requests
|
|
+ * using pci_pool_alloc API
|
|
+ *- If failed to allocate, return -ENOMEM
|
|
+ * @return none
|
|
+ */
|
|
+static int init_dma_pools(struct ioh_udc_dev *dev)
|
|
+{
|
|
+ struct ioh_udc_stp_dma_desc *td_stp;
|
|
+ struct ioh_udc_data_dma_desc *td_data;
|
|
+
|
|
+ /* DMA setup */
|
|
+ dev->data_requests = pci_pool_create("data_requests", dev->pdev,
|
|
+ sizeof(struct ioh_udc_data_dma_desc), 0, 0);
|
|
+ if (dev->data_requests == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "init_dma_pools: can't get request\
|
|
+ data pool");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* dma desc for setup data */
|
|
+ dev->stp_requests = pci_pool_create("setup requests", dev->pdev,
|
|
+ sizeof(struct ioh_udc_stp_dma_desc), 0, 0);
|
|
+ if (dev->stp_requests == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "init_dma_pools: can't get setup\
|
|
+ request pool");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ /* setup */
|
|
+ td_stp = pci_pool_alloc(dev->stp_requests, GFP_KERNEL,
|
|
+ &dev->ep[UDC_EP0OUT_IDX].td_stp_phys);
|
|
+ if (td_stp == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "init_dma_pools: can't allocate setup\
|
|
+ dma descriptor");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ dev->ep[UDC_EP0OUT_IDX].td_stp = td_stp;
|
|
+
|
|
+ /* data: 0 packets !? */
|
|
+ td_data = pci_pool_alloc(dev->data_requests, GFP_KERNEL,
|
|
+ &dev->ep[UDC_EP0OUT_IDX].td_data_phys);
|
|
+ if (td_data == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "init_dma_pools: can't allocate data dma\
|
|
+ descriptor");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ dev->ep[UDC_EP0OUT_IDX].td_data = td_data;
|
|
+ dev->ep[UDC_EP0IN_IDX].td_stp = NULL;
|
|
+ dev->ep[UDC_EP0IN_IDX].td_stp_phys = 0;
|
|
+ dev->ep[UDC_EP0IN_IDX].td_data = NULL;
|
|
+ dev->ep[UDC_EP0IN_IDX].td_data_phys = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/* Called by pci bus driver to init pci context */
|
|
+/*!@ingroup UDC_PCILayerAPI
|
|
+ * @fn static int ioh_udc_probe(struct pci_dev *pdev,
|
|
+ * const struct pci_device_id *id)
|
|
+ * @brief Implements the Probe routine for IOH USB device controller driver
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @param id [@ref IN] reference to struct pci_device_id table
|
|
+ * @return int [ 0 on Success and linux error number on failure ]
|
|
+ * @remarks The following actions are performed:
|
|
+ *- Allocate and initialize the device driver data structures
|
|
+ *- Enable the PCI device and set driver private data
|
|
+ *- Do PCI resource allocation for the device
|
|
+ *- Request memory region for the device
|
|
+ *- Map PCI device memory to kernel virtual space
|
|
+ *- Initialize the HAL layer by invoking ioh_udc_pcd_init API
|
|
+ *- Register the interrupt handler ioh_udc_isr
|
|
+ *- Enable Bus mastering for the device using pci_set_master API.
|
|
+ *- Enable memory write invalidate PCI transaction using pci_try_set_mwi API.
|
|
+ *- Set up device structure and ops structure and initialize the gadget driver
|
|
+ * data structure
|
|
+ *- If using DMA (specified during module loading), initialize DMA pools using
|
|
+ * init_dma_pools API
|
|
+ *- Register the device with the system using device_register API
|
|
+ *- Put the device in disconnected state till a driver is bound, using
|
|
+ * ioh_udc_set_disconnect API
|
|
+ *- Invoke ioh_udc_remove to perform clean-up on any error.
|
|
+ */
|
|
+static int ioh_udc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
+{
|
|
+ unsigned long resource;
|
|
+ unsigned long len;
|
|
+ int retval = 0;
|
|
+ struct ioh_udc_dev *dev;
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_probe: enter");
|
|
+ /* one udc only */
|
|
+ if (ioh_udc != NULL) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: already probed");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ /* init */
|
|
+ dev = kzalloc(sizeof(struct ioh_udc_dev), GFP_KERNEL);
|
|
+ if (dev == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: no memory for device\
|
|
+ structure");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memset(dev, 0, sizeof(struct ioh_udc_dev));
|
|
+ /* pci setup */
|
|
+ if (pci_enable_device(pdev) < 0) {
|
|
+ kfree(dev);
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: pci_enable_device failed");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ dev->active = 1;
|
|
+ pci_set_drvdata(pdev, dev);
|
|
+
|
|
+ /* PCI resource allocation */
|
|
+ resource = pci_resource_start(pdev, 1);
|
|
+ len = pci_resource_len(pdev, 1);
|
|
+ IOH_DEBUG("ioh_udc_probe: resource %lx, len %ld", resource, len);
|
|
+
|
|
+ if (request_mem_region(resource, len, name) == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: pci device used already");
|
|
+ retval = -EBUSY;
|
|
+ goto finished;
|
|
+ }
|
|
+ dev->phys_addr = resource;
|
|
+ dev->mem_region = 1;
|
|
+
|
|
+ dev->virt_addr = ioremap_nocache(resource, len);
|
|
+ if (dev->virt_addr == NULL) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: device memory cannot be\
|
|
+ mapped");
|
|
+ retval = -ENOMEM;
|
|
+ goto finished;
|
|
+ }
|
|
+ IOH_DEBUG("ioh_udc_probe: device memory mapped at %x",
|
|
+ (int)dev->virt_addr);
|
|
+
|
|
+ if (pdev->irq == 0) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: irq not set");
|
|
+ retval = -ENODEV;
|
|
+ goto finished;
|
|
+ }
|
|
+
|
|
+ ioh_udc = dev;
|
|
+
|
|
+ /* initialize the hardware */
|
|
+ if (ioh_udc_pcd_init(dev) != 0)
|
|
+ goto finished;
|
|
+
|
|
+
|
|
+ if (request_irq(pdev->irq, ioh_udc_isr, IRQF_SHARED, name, dev) != 0) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_probe: request_irq(%d) fail",
|
|
+ pdev->irq);
|
|
+ retval = -ENODEV;
|
|
+ goto finished;
|
|
+ }
|
|
+ dev->irq = pdev->irq;
|
|
+ dev->irq_registered = 1;
|
|
+
|
|
+ pci_set_master(pdev);
|
|
+ pci_try_set_mwi(pdev);
|
|
+
|
|
+ /* device struct setup */
|
|
+ spin_lock_init(&dev->lock);
|
|
+ dev->pdev = pdev;
|
|
+ dev->gadget.ops = &ioh_udc_ops;
|
|
+
|
|
+ /* init dma pools */
|
|
+ retval = init_dma_pools(dev);
|
|
+ if (retval != 0)
|
|
+ goto finished;
|
|
+
|
|
+/* strcpy(dev->gadget.dev.bus_id, "gadget");*/
|
|
+ dev_set_name(&dev->gadget.dev, "gadget");
|
|
+ dev->gadget.dev.parent = &pdev->dev;
|
|
+ dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
|
+ dev->gadget.dev.release = gadget_release;
|
|
+ dev->gadget.name = name;
|
|
+ dev->gadget.is_dualspeed = 1;
|
|
+
|
|
+ retval = device_register(&dev->gadget.dev);
|
|
+ if (retval != 0)
|
|
+ goto finished;
|
|
+
|
|
+ dev->registered = 1;
|
|
+
|
|
+ /* Put the device in disconnected state till a driver is bound */
|
|
+ ioh_udc_set_disconnect(dev->regs);
|
|
+
|
|
+#ifdef IOH_PRINT_REG
|
|
+ /* print dev register info */
|
|
+ ioh_udc_print_regs((u32)dev->virt_addr);
|
|
+#endif
|
|
+ return 0;
|
|
+
|
|
+finished:
|
|
+ ioh_udc_remove(pdev);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_PCILayerAPI
|
|
+ * @fn static void ioh_udc_shutdown(struct pci_dev *pdev)
|
|
+ * @brief This routine makes sure that the device is quiescent.
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @return none
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Disable interrupts from the device
|
|
+ * - Put the PCI device in soft disconnect mode
|
|
+ */
|
|
+static void ioh_udc_shutdown(struct pci_dev *pdev)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_shutdown enter");
|
|
+
|
|
+ ioh_udc_disable_interrupts(dev->regs, UDC_DEVINT_MSK);
|
|
+ ioh_udc_disable_ep_interrupts(dev->regs, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+
|
|
+ /* disable the pullup so the host will think we're gone */
|
|
+ ioh_udc_set_disconnect(dev->regs);
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_PCILayerAPI
|
|
+ * @fn static int ioh_udc_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
+ * @brief Implements the Suspend functionality for IOH USB device controller
|
|
+ * driver
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @param state [@ref IN] specifies new PM state to which to transition to
|
|
+ * @return int [ 0 on Success and linux error number on failure ]
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Disable interrupts from the device
|
|
+ * - Disable the PCI device
|
|
+ * - Save the PCI state
|
|
+ * - Transition to new power state
|
|
+ */
|
|
+static int ioh_udc_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
+{
|
|
+ struct ioh_udc_dev *dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ IOH_DEBUG("ioh_udc_suspend enter");
|
|
+
|
|
+ ioh_udc_disable_interrupts(dev->regs, UDC_DEVINT_MSK);
|
|
+ ioh_udc_disable_ep_interrupts(dev->regs, UDC_EPINT_MSK_DISABLE_ALL);
|
|
+
|
|
+ pci_disable_device(pdev);
|
|
+ pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
+
|
|
+ if (pci_save_state(pdev) != 0) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_suspend: could not save PCI config\
|
|
+ state");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ if (pci_set_power_state(pdev, pci_choose_state(pdev, state)) == -EIO)
|
|
+ IOH_DEBUG("ioh_udc_suspend: does not support PM cpabilities");
|
|
+
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*!@ingroup UDC_PCILayerAPI
|
|
+ * @fn static int ioh_udc_resume(struct pci_dev *pdev)
|
|
+ * @brief Implements the Resume functionality for IOH USB device
|
|
+ * controller driver
|
|
+ * @param pdev [@ref IN] reference to struct pci_dev
|
|
+ * @return int [ 0 on Success and linux error number on failure ]
|
|
+ * @remarks The main tasks performed by this method are:
|
|
+ * - Set power state to PCI_D0 using pci_set_power_state
|
|
+ * - Restore the device state using pci_restore_state
|
|
+ * - Enable the device using pci_enable_device
|
|
+ * - Enable the bus mastering using pci_set_master
|
|
+ * - Disable the PM notifications using pci_enable_wake
|
|
+ */
|
|
+static int ioh_udc_resume(struct pci_dev *pdev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = pci_set_power_state(pdev, PCI_D0);
|
|
+ if (ret != 0)
|
|
+ IOH_DEBUG("ioh_udc_resume: does not support PM cpabilities");
|
|
+
|
|
+
|
|
+ ret = pci_restore_state(pdev);
|
|
+ if (ret != 0) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_resume: pci_restore_state failed");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = pci_enable_device(pdev);
|
|
+
|
|
+ if (ret != 0) {
|
|
+ IOH_LOG(KERN_ERR, "ioh_udc_resume: pci_enable_device failed");
|
|
+ return ret;
|
|
+ }
|
|
+ pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* PCI device parameters */
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ * @struct ioh_udc_pcidev_id
|
|
+ * @brief This is an instance of pci_device_id structure which holds
|
|
+ * information
|
|
+ * about the PCI USB device that are supported by this
|
|
+ * driver.
|
|
+ */
|
|
+static const struct pci_device_id ioh_udc_pcidev_id[] = {
|
|
+ {
|
|
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOH1_UDC),
|
|
+ .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
|
|
+ .class_mask = 0xffffffff,
|
|
+ },
|
|
+ {
|
|
+ PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ROHM_IOH2_UDC),
|
|
+ .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
|
|
+ .class_mask = 0xffffffff,
|
|
+ },
|
|
+ { 0 },
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(pci, ioh_udc_pcidev_id);
|
|
+
|
|
+/* PCI functions */
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ * @struct ioh_udc_driver
|
|
+ * @brief Linux pci_driver structure which provides the reference to
|
|
+ * PCI methods of this driver
|
|
+ */
|
|
+static struct pci_driver ioh_udc_driver = {
|
|
+ .name = (char *) name,
|
|
+ .id_table = ioh_udc_pcidev_id,
|
|
+ .probe = ioh_udc_probe,
|
|
+ .remove = ioh_udc_remove,
|
|
+ .suspend = ioh_udc_suspend,
|
|
+ .resume = ioh_udc_resume,
|
|
+ .shutdown = ioh_udc_shutdown,
|
|
+};
|
|
+
|
|
+/* Initialize the driver */
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ * @fn static int __init ioh_udc_pci_init(void)
|
|
+ * @brief This function is the entry point for the driver
|
|
+ * @param none
|
|
+ * @return int [ 0 on success and <0 on failure ]
|
|
+ */
|
|
+static int __init ioh_udc_pci_init(void)
|
|
+{
|
|
+ return pci_register_driver(&ioh_udc_driver);
|
|
+}
|
|
+module_init(ioh_udc_pci_init);
|
|
+
|
|
+/* Cleans driver */
|
|
+/*!@ingroup UDC_InterfaceLayerAPI
|
|
+ * @fn static void __exit ioh_udc_pci_exit(void)
|
|
+ * @brief This function is the exit point for the driver
|
|
+ * @param none
|
|
+ * @return none
|
|
+ */
|
|
+static void __exit ioh_udc_pci_exit(void)
|
|
+{
|
|
+ pci_unregister_driver(&ioh_udc_driver);
|
|
+}
|
|
+module_exit(ioh_udc_pci_exit);
|
|
+
|
|
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
|
+MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/drivers/usb/gadget/pch_udc_pci.h
|
|
@@ -0,0 +1,97 @@
|
|
+ /*!
|
|
+ * @file ioh_udc_pci.h
|
|
+ * @brief
|
|
+ * The IOH UDC is a USB High speed DMA capable USB device controller.
|
|
+ * It provides 4 IN and 4 OUT endpoints (control, bulk isochronous or interrupt
|
|
+ * type).
|
|
+ *
|
|
+ * The IOH USB device controller driver provides required interface
|
|
+ * to the USB gadget framework for accessing the IOH USB device hardware.
|
|
+ *
|
|
+ * @version 0.96
|
|
+ *
|
|
+ * @section
|
|
+ * 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 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * History:
|
|
+ * Copyright (C) 2009 OKI SEMICONDUCTOR Co., LTD.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * created:
|
|
+ * OKI SEMICONDUCTOR 2/26/2010
|
|
+ * modified:
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef IOH_UDC_PCI_H
|
|
+#define IOH_UDC_PCI_H
|
|
+
|
|
+#include <linux/types.h>
|
|
+#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/smp_lock.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/init.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 <asm/byteorder.h>
|
|
+#include <asm/system.h>
|
|
+#include <asm/unaligned.h>
|
|
+
|
|
+/* gadget stack */
|
|
+#include <linux/usb/ch9.h>
|
|
+#include <linux/usb/gadget.h>
|
|
+#include "pch_udc_hal.h"
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ * @def PCI_VENDOR_ID_INTEL
|
|
+ * @brief PCI Vendor ID for Intel.
|
|
+ */
|
|
+#define PCI_VENDOR_ID_INTEL 0x8086
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ * @def PCI_VENDOR_ID_ROHM
|
|
+ * @brief PCI Vendor ID for ROHM.
|
|
+ */
|
|
+#define PCI_VENDOR_ID_ROHM 0x10db
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ * @def PCI_DEVICE_ID_INTEL_IOH1_UDC
|
|
+ * @brief Outlines the PCI Device ID for Intel IOH GE UDC device.
|
|
+ */
|
|
+#define PCI_DEVICE_ID_INTEL_IOH1_UDC 0x8808 /*Device Id for GE device*/
|
|
+
|
|
+/*!@ingroup UDC_InterfaceLayer
|
|
+ * @def PCI_DEVICE_ID_INTEL_IOH1_UDC
|
|
+ * @brief Outlines the PCI Device ID for ROHM IOH IVI UDC device.
|
|
+ */
|
|
+#define PCI_DEVICE_ID_ROHM_IOH2_UDC 0x801D /* Device ID for IVI*/
|
|
+
|
|
+extern u32 ioh_udc_base;
|
|
+#endif /* #ifdef IOH_UDC_PCI_H */
|