From: Masayuki Ohtake Subject: OKI Semiconductor PCH USB Gadget driver This driver implements PCH Gadget controls for PCH. Signed-off-by: Masayuki Ohtake Acked-by: Wang Qi --- 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 +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +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 +#include +#include +#include +/*#include */ +#include +#include "pch_common.h" +#include "pch_debug.h" + +#include +#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 + +/*! @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. +
+*/ + +/*! @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. +
+*/ + +/*! @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. +
+*/ + +/*! @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. +
+*/ + +/*! @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. +
+*/ + +/*! @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. +
+*/ + +/*! @defgroup UDC_PCILayerFacilitators +@ingroup UDC_PCILayer +@brief This group contains the data structures used by the PCI + Layer APIs for their functionalities. +
+*/ + +/*! @defgroup UDC_InterfaceLayerAPI +@ingroup UDC_InterfaceLayer +@brief This group contains the API(functions) used as the Driver + +
+*/ + +/*! @defgroup UDC_InterfaceLayerFacilitators +@ingroup UDC_InterfaceLayer +@brief This group contains the data structures used by the Driver + interface APIs for their functionalities. +
+*/ + +/*! @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. +
+*/ + +/*! @defgroup UDC_UtilitiesAPI +@ingroup UDC_Utilities +@brief This group contains the APIs(functions) used by other functions + in their operations. +
+*/ + + +/* 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 +#include +#include +#include +#include +/*#include */ +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* gadget stack */ +#include +#include +#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 */