PATCH: Add UDC support for MIPS/AU1200 and Geode/CS5536 This patch adds UDC support for the AU1200 processor and the Geode CS5536 southbridge. Signed-off-by: Jordan Crouse --- Documentation/usb/au1200udc.txt | 185 + drivers/usb/gadget/Kconfig | 86 + drivers/usb/gadget/Makefile | 9 drivers/usb/gadget/au1200otg.h | 147 + drivers/usb/gadget/au1200udc.c | 4880 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/au1200udc.h | 936 +++++++ drivers/usb/gadget/au1200uoc.c | 2015 +++++++++++++++ drivers/usb/gadget/au1200uoc.h | 1338 ++++++++++ drivers/usb/gadget/ether.c | 65 drivers/usb/gadget/file_storage.c | 8 drivers/usb/gadget/gadget_chips.h | 8 drivers/usb/gadget/zero.c | 10 12 files changed, 9684 insertions(+), 3 deletions(-) diff --git a/Documentation/usb/au1200udc.txt b/Documentation/usb/au1200udc.txt new file mode 100644 index 0000000..20f4d58 --- /dev/null +++ b/Documentation/usb/au1200udc.txt @@ -0,0 +1,185 @@ +------------------------------------------------------------------------------- +Howto for Linux device driver for the AU1200 USB Device Controller (UDC) +for gadget driver stack +------------------------------------------------------------------------------- + +Author: Thomas Dahlmann + +INTRODUCTION: + +The AU1200 UDC is part of AMD MIPS CPU Au1200 and AMD5536 chip (south- +bridge of AMD Geode LX Processor). +It is a DMA capable usb device controller. The usb port is shared +between host and UDC. The Au1200 on-chip OTG controller is used to switch the +usb port between host, UDC and neutral. So au1200otg driver is needed +to get the UDC operating. + +Current gadget support: file_storage, ether, zero + +------------------------------------------------------------------------------- +WHAT YOU NEED: +------------------------------------------------------------------------------- + +copy/replace following files to /usr/src/linux/drivers/usb/gadget + au1200udc.c + au1200udc.h + au1200otg.h + au1200uoc.c + au1200uoc.h + ether.c + file_storage.c + zero.c + gadget_chips.h + Makefile + Kconfig + +------------------------------------------------------------------------------- +HOW TO INSTALL IT: +------------------------------------------------------------------------------- + +change to directory /usr/src/linux + +configure gadget as module: + "make menuconfig" + under "Code maturity level options" choose "Prompt for development ..." + goto "Device Drivers" + goto "USB support" -> "USB Gadget support" + choose "Support for USB Gadgets" as module + under choice "USB Peripheral Controller" choose "AU1200UDC" + under "USB Gadget Drivers" + choose "Gadget Zero" as module or + choose "Ethernet Gagdet" as module or + choose "File-backed Storage Gadget" as module + choose "AU1200 USB portmux control (On-The-Go support)" + under choice "OTG port functionality" choose "Gadget only" + exit and save config + +compile and install modules: + "make modules modules_install" + +------------------------------------------------------------------------------- +HOW TO USE IT: +------------------------------------------------------------------------------- + +*** How to load FILE-BACKED STORAGE gadget driver - mass storage *** +enable USB mass storage support for linux host: + change to directory /usr/src/linux + "make menuconfig" + under "File systems" choose "DOS/FAT/NT filesystems" + choose "MSDOS fs support" + under "Device Drivers" under "SCSI device support" + choose "SCSI device support" + choose "SCSI generic support" + under "Device Drivers" under "USB support" in section + "USB Device Class drivers" choose + "USB Mass Storage support" + compile new kernel +create disk file: + "dd bs=1M count=128 if=/dev/zero of=/tmp/disk" + => creates a 128Mbyte image file /tmp/disk +load modules: + "modprobe au1200udc" + "modprobe g_file_storage file=/tmp/disk" + "modprobe au1200otg" +create a primary FAT16 disk partition via linux host site: + "fdisk /dev/sda", make FAT16 prim. partition + => "n", "p", "1", "", "", "t", "6", "w", + "mkdosfs /dev/sda1" + "sync" +create primary disk partition via Windows XP host site: + right click on "My Computer" + choose "Manage" => "Disk Management" + choose usb disk and follow instructions of partition menu +mount usb mass storage device on linux host: + make directory "/mnt/gadget/" + "mount -t msdos /dev/sda1 /mnt/gadget" + +*** How to access files of disk image on UDC side *** +When files were copied from host to UDC mass storage device then files +are inside the monolitic disk image (see above) on UDC side. This +disk image can be mounted via the loopback device driver to a +directory on UDC side to access these files. +Steps on UDC side: + enable kernel support for loopback device + change to directory /usr/src/linux + "make menuconfig" + under "Block devices" choose "Loopback device support" + and recompile the kernel + determine offset inside disk image: + "fdisk -l -u disk_image", output is like: +>> You must set cylinders. +>> You can do this from the extra functions menu. +>> +>> Disk /tmp/disk128: 5 heads, 52 sectors, 0 cylinders +>> Units = sectors of 1 * 512 bytes +>> +>> Device Boot Start End Blocks Id System +>> /tmp/disk128p1 52 262079 131014 6 FAT16 + + get offset my multiplying start value by sector size: + 52 * 512 = 26624 + mount disk image: + "mount -o loop,offset=26624 -t msdos disk_image /mnt" + + +*** How to load ZERO gadget driver - simple BULK loop back *** +load modules: +UDC driver: + (a) Slave/Fifo mode: "modprobe au1200udc use_dma=0" + (b) DMA Buffer Fill mode: "modprobe au1200udc use_dma=1" + default: + (c) DMA PPBNDU mode: "modprobe au1200udc" + (d) DMA PPBDU mode: "modprobe au1200udc use_dma=1 use_dma_ppb=1 use_dma_ppb_du=1" + (e) fullspeed mode: "modprobe au1200udc use_fullspeed=1", can be combined + with all dma modes + (f) special higspeed + tx buffer size: "modprobe au1200udc hs_tx_buf=" + example: "modprobe au1200udc use_dma=1 hs_tx_buf=128" + => buffer size = 512 bytes (=bulk max packet) +Gadget Zero driver: + (a) Bulk loop: "modprobe g_zero" + (b) Int loop: "modprobe g_zero use_interrupt_traffic=1" + (c) Source/Sink "modprobe g_zero loopdefault=0" + OUT data must all be zero's + (d) Source/Sink count "modprobe g_zero loopdefault=0 pattern=1" + each OUT packet must count modulo63 (0,1,..,62,0,1,..) +OTG driver: "modprobe au1200otg" +example: + "modprobe au1200udc" + "modprobe g_zero" + "modprobe au1200otg" + => loads driver for DMA PPBNDU mode and Bulk loop + + +*** How to use ETHERNET gadget driver (CDC protocol) *** + with Linux Host +UDC side bringup: + load gadget modules + "modprobe au1200udc" + "modprobe g_ether" + "modprobe au1200otg" + "ifconfig usb0 192.168.0.2" +Host side bringup: + install support for CDC Ethernet: + change to directory /usr/src/linux + "make menuconfig" + under "Device Drivers" under "USB support" under + "USB Network adapters" choose + "Multi-purpose USB Networking Framework" + choose "CDC Ethernet support" + compile mew kernel + "modprobe CDCEther" + "ifconfig eth1 192.168.0.1" + note: assuming there is one network card assigned to eth0, + otherwise kernel messages of CDC Ethernet module show + which interface (ethX) is used +Use network connection: + after bringup of host and UDC side it behaves like a normal + ethernet connection between host and UDC + test the connection: + host side: "ping -I eth1 192.168.0.2" + UDC side : "ping -I usb0 192.168.0.1" + note: the "-I" option assures that the USB cable is used, the + option can be omitted when the routing table is setup to avoid + using other network interfaces as eth0 + diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ff075a5..2a3b652 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -89,7 +89,27 @@ config USB_NET2280 depends on USB_GADGET_NET2280 default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_AU1200UDC + boolean "AU1200UDC" + depends on PCI || SOC_AU1200 + select USB_GADGET_DUALSPEED + help + AU1200 UDC is a PCI based USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + + It has four configurable endpoints, as well as endpoint zero + (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "au1200udc" and force all + gadget drivers to also be dynamically linked. +config USB_AU1200UDC + tristate + depends on USB_GADGET_AU1200UDC + default USB_GADGET + select USB_GADGET_SELECTED + select USB_PORT_AU1200OTG config USB_GADGET_PXA2XX boolean "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX @@ -322,7 +342,7 @@ config USB_ETH config USB_ETH_RNDIS bool "RNDIS support (EXPERIMENTAL)" - depends on USB_ETH && EXPERIMENTAL + depends on USB_ETH && EXPERIMENTAL && !(USB_GADGET_AU1200UDC) default y help Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, @@ -395,4 +415,68 @@ config USB_G_SERIAL endchoice +# +# AU1200 USB OTG options +# +config USB_PORT_AU1200OTG + boolean "AU1200 USB portmux control (On-The-Go support)" + depends on USB_GADGET_AU1200UDC || USB_EHCI_HCD || USB_OHCI_HCD + default n + help + The AU1200 and Au1200 USB device port can be used as + either a host port or a device port. + Say "y" here if you want to use both or if you want to + setup this port to fit into your board configuration. + + If the AU1200UDC driver is configured to be statically + linked this module will be linked into the kernel image + as well otherwise an additional module called + "au1200otg" will be built. + +config USB_AU1200OTG + tristate + depends on USB_PORT_AU1200OTG + default USB_AU1200UDC + +choice + prompt "OTG port functionality" + depends on USB_PORT_AU1200OTG + default USB_OTGMUX_GADGET + +config USB_OTGMUX_HOST + boolean "Host only" + depends on !USB_GADGET_AU1200UDC && (USB_EHCI_HCD || USB_OHCI_HCD) + help + Select this if your device port is connected to a + Standard-A or a Mini-A connector. + It is possible to choose this with a Mini-AB connector + if the device controller is not to be used. + +config USB_OTGMUX_GADGET + boolean "Gadget only" + depends on USB_GADGET_AU1200UDC + help + Select this if your device port is connected to a + Standard-B or a Mini-B connector. + It is possible to choose this with a Mini-AB connector + if the host controller is not to be used at this port. + +config USB_OTGMUX_IDPIN + boolean "Cable select" + depends on USB_GADGET_AU1200UDC && (USB_EHCI_HCD || USB_OHCI_HCD) + help + Choose this if you want to use both host and device at + this port. The port multiplexer is controlled by the + connectors ID pin. If an A-plug is connected the port + it is routed to the host otherwise it's routed to the + device controller. + + This is useful for validation platforms which use AB + receptacle in order to share one port between host and device. + It is not a USB compliant option. + + Select this only if your device port is connected to + a Mini-AB connector. + +endchoice endmenu diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index d5fd04d..0298250 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2280) += net2280.o +obj-$(CONFIG_USB_AU1200UDC) += au1200udc.o obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o @@ -28,3 +29,11 @@ obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o +# +# AU1200 USB OTG options +# +ifeq ($(CONFIG_USB_PORT_AU1200OTG),y) +obj-$(CONFIG_USB_AU1200OTG) += au1200otg.o + +au1200otg-objs := au1200uoc.o +endif diff --git a/drivers/usb/gadget/au1200otg.h b/drivers/usb/gadget/au1200otg.h new file mode 100644 index 0000000..2b05703 --- /dev/null +++ b/drivers/usb/gadget/au1200otg.h @@ -0,0 +1,147 @@ +/* + * AMD Au1200 USB OTG driver + */ + +/* + * Copyright (C) 2005 AMD (http://www.amd.com) + * Author: Karsten Boge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + */ +#ifndef AU1200OTG_H +#define AU1200OTG_H + +/* log Bug 5395 */ +#define OTG_HNP_ERR_LOG + +/***************************************************************************** +* Constants +*****************************************************************************/ + + +/********************************** + * OTG sub-state definitions +***********************************/ + +#define OTG_STATE_MASK 0x0F + +#define OTG_STATE_B_HOST_WT (0x10 | OTG_STATE_B_HOST) +#define OTG_STATE_B_PERIPHERAL_WT (0x10 | OTG_STATE_B_PERIPHERAL) +#define OTG_STATE_B_PERIPHERAL_DC (0x20 | OTG_STATE_B_PERIPHERAL) +#define OTG_STATE_B_SRP_WAIT_SE0 (0x10 | OTG_STATE_B_SRP_INIT) +#define OTG_STATE_B_SRP_D_PULSE (0x20 | OTG_STATE_B_SRP_INIT) +#define OTG_STATE_B_SRP_V_PULSE (0x30 | OTG_STATE_B_SRP_INIT) +#define OTG_STATE_B_SRP_V_DCHRG (0x40 | OTG_STATE_B_SRP_INIT) +#define OTG_STATE_B_SRP_WAIT_VBUS (0x50 | OTG_STATE_B_SRP_INIT) + +#define OTG_STATE_A_IDLE_WAIT_DP (0x10 | OTG_STATE_A_IDLE) +#define OTG_STATE_A_IDLE_WAIT_VP (0x20 | OTG_STATE_A_IDLE) +#define OTG_STATE_A_IDLE_WAIT_MP (0x30 | OTG_STATE_A_IDLE) +#define OTG_STATE_A_IDLE_WAIT_DV (0x40 | OTG_STATE_A_IDLE) +#define OTG_STATE_A_WAIT_BCON_VB (0x10 | OTG_STATE_A_WAIT_BCON) +#define OTG_STATE_A_WAIT_VFALL_DN (0x10 | OTG_STATE_A_WAIT_VFALL) + + +/********************************** +* typical timer values +**********************************/ + +#define OTG_TMR_WAIT_VFALL 10 /* ( ) A waits for VBus */ +#define OTG_TMR_A_WAIT_VRISE 100 /* ( ) A waits for VBus */ +#define OTG_TMR_A_WAIT_BCON 200 /* ( ) A waits for B-connect (1.. s) */ +#define OTG_TMR_A_IDLE_BDIS 250 /* (ms) A waits for B-disc (200.. ms) */ +#define OTG_TMR_B_WAIT_ADISCON 600 /* (us) B waits for A to disconnect <1ms */ +#define OTG_TMR_B_ACON_BRST 200 /* (us) B waits before starting reset */ +#define OTG_TMR_B_ASE0_BRST 5 /* (ms) B waits for A-conn (3.125.. ms) */ +#define OTG_TMR_B_AIDL_BDIS 50 /* (ms) B waits before dc (5..150ms) */ +#define OTG_TMR_SRP_WAIT_SE0 2 /* ( ) B SRP idle wait */ +#define OTG_TMR_SRP_WAIT_DP 7 /* (ms) B SRP D_PULSE (5..10ms) */ +#define OTG_TMR_SRP_WAIT_VP 80 /* (ms) B SRP V_PULSE (5..100ms) */ +#define OTG_TMR_SRP_DCHRG_V 30 /* ( ) B SRP VBus discharge */ +#define OTG_TMR_SRP_WAIT_VRS 5800 /* (ms) B SRP waits for VBus (5..6s) */ +#define OTG_TMR_ASRP_WAIT_MP 4 /* ( ) A SRP min. pulse */ +#define OTG_TMR_ASRP_WAIT_DP 10 /* (ms) A SRP D_PULSE TO */ +#define OTG_TMR_ASRP_WAIT_VP 200 /* (ms) A SRP V_PULSE TO */ +#define OTG_TMR_ASRP_WAIT_DV 200 /* ( ) A SRP waits for V_PULSE */ +#define OTG_TMR_A_BCON_VB 50 /* ( ) A waits for VBus after connect */ + +#define OTG_TMR_IDSNS_WAIT 10 /* (ms) ID sense wait */ + +/*********************************/ + +#define TIMER_PERIOD 1000 /* 10 ms, if longer than 10ms */ + +/********************************** + * OTG state parameters + **********************************/ + +#define OTG_HOST_READY (1<<20) /* indicates a USB host driver is */ + /* running */ +#define OTG_GADGET_READY (1<<21) /* indicates a USB gadget driver is */ + /* running */ +#define OTG_A_BUS_REQ (1<<22) /* used by appl-SW to request a */ + /* VBus rise, auto-reset by driver */ +#define OTG_A_BUS_DROP (1<<23) /* used by appl-SW to request a */ + /* VBus drop, auto-reset by driver */ +#define OTG_A_CLR_ERR (1<<24) /* used by appl-SW to request VBerr */ + /* clean-up, auto-reset by driver */ +#define OTG_AB_HNP_REQ (1<<25) /* used by appl-SW to initiate */ + /* HNP, auto-reset by driver */ +#define OTG_B_BUS_REQ (1<<26) /* used by appl-SW to request */ + /* B-device functionality, ... */ +#define OTG_B_BUS_DIS (1<<27) /* used by appl-SW to request */ + /* disable B-device functionality */ +#define OTG_B_aSSN_REQ (1<<28) /* used by appl-SW to initiate SRP, */ + /* auto-reset by the driver */ +#define OTG_B_SRP_ERROR (1<<29) /* indicates invalid HW conditions */ + /* during SRP, reset by writing "1" */ +#define OTG_A_VBUS_FAILED (1<<30) /* indicates a VBus error, reset by */ + /* writing "1", when setting */ + /* CLR_ERR or when leaving A-states */ +#define OTG_UDC_RWK_REQ (1<<31) /* call UDC function to force a */ + /* remote wake-up */ + +#define SW_REQUEST_MASK (OTG_A_BUS_REQ | OTG_A_BUS_DROP | \ + OTG_A_CLR_ERR | OTG_B_aSSN_REQ | \ + OTG_B_BUS_REQ | OTG_B_BUS_DIS | \ + OTG_UDC_RWK_REQ) + +/*********************************************************************/ + +/* + * gadget events for notify function + */ +#define OTG_GADGET_EVT_SVDROP (1<<0) /* Session valid drop */ +#define OTG_GADGET_EVT_SVALID (1<<1) /* Session valid */ +#define OTG_GADGET_REQ_WAKE (1<<2) /* Request remote wake-up */ + +/*** HS-A0 WA: gadget phy suspend issue ***/ +#define OTG_FLAGS_UDC_SUSP (1<<17) /* gadget phy suspended */ + +#ifdef OTG_HNP_ERR_LOG +#define OTG_FLAGS_HNP_ERR (1<<18) /* HNP error bug 5395 occured */ +#endif + +/***************************************************************************** +* Data +*****************************************************************************/ + +struct usb_otg_gadget_extension { + int (*request) (u32); /* function call for state change requests */ + u32 (*query) (int); /* function call to query state */ + void (*notify) (u32); /* filled in by gadget for notification */ +}; + +#endif /* AU1200OTG_H */ diff --git a/drivers/usb/gadget/au1200udc.c b/drivers/usb/gadget/au1200udc.c new file mode 100644 index 0000000..7153902 --- /dev/null +++ b/drivers/usb/gadget/au1200udc.c @@ -0,0 +1,4880 @@ +/* + * AMD Au1200 UDC high/full speed USB device controller driver. + * + * Note: Driver also works with AMD Geode LX southbridge AMD5536 + */ + +/* + * Copyright (C) 2005 AMD (http://www.amd.com) + * Author: Thomas Dahlmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +/***************************************************************************** + * Defines + *****************************************************************************/ + +/* debug control */ +#undef UDC_DEBUG +/* #define UDC_DEBUG 1 */ +#undef UDC_VERBOSE +/* #define UDC_VERBOSE */ +/* #define UDC_REGISTER_DUMP */ + +/* Driver strings */ +#define UDC_MOD_DESCRIPTION "AMD Au1200 UDC - USB Device Controller" +#define UDC_DRIVER_VERSION_STRING "01.00.0204 - $Revision: #4 $" + +/***************************************************************************** + * Includes + *****************************************************************************/ + +/* system */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MIPS config */ +#ifdef CONFIG_SOC_AU1200 +#include +#include +#endif + +/* gadget stack */ +#include +#include +#include + +/* udc/uoc specific */ +#include "au1200udc.h" +#include "au1200uoc.h" + + +/* use RDE timer for new kernel only */ +#define UDC_USE_TIMER +/***************************************************************************** + * Static Function Declarations + *****************************************************************************/ + +void udc_tasklet_disconnect(unsigned long); +#ifdef UDC_USE_TIMER +void udc_timer_function(unsigned long v); +void udc_pollstall_timer_function(unsigned long v); +#endif +static int udc_rxfifo_read_dwords(struct udc* dev, u32* buf, int dwords); +static void empty_req_queue (struct udc_ep *); +static int udc_probe (struct udc* dev); +static void udc_basic_init (struct udc *dev); +static void udc_setup_endpoints (struct udc *dev); +static void udc_soft_reset(struct udc* dev); +#ifdef UDC_DEBUG +static void dump_buffer(u8* buf, u32 bytes); +static int udc_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int udc_open (struct inode *inode, struct file *file); +static int udc_release (struct inode *inode, struct file *file); +#endif +static struct udc_data_dma* udc_get_last_dma_desc(struct udc_request* req); +static u32 udc_get_ppbdu_rxbytes(struct udc_request* req); +static int udc_free_dma_chain(struct udc* dev, struct udc_request* req); +static int udc_create_dma_chain(struct udc_ep* ep, struct udc_request* req, unsigned long buf_len, int gfp_flags); +static inline int startup_registers(struct udc* dev); +#ifdef CONFIG_SOC_AU1200 +void au1200_sync(void); +#endif +static int udc_remote_wakeup(struct udc* dev); +#if defined(CONFIG_SOC_AU1200) +static int udc_suspend(struct udc* dev); +static int udc_resume(struct udc* dev); +#else +static int udc_pci_probe (struct pci_dev *pdev, const struct pci_device_id *id); +static void udc_pci_remove (struct pci_dev *pdev); +#endif + +/***************************************************************************** + * Data + *****************************************************************************/ + +/* description */ +static const char mod_desc [] = UDC_MOD_DESCRIPTION; +static const char name [] = DRIVER_NAME_FOR_PRINT; + +/* structure to hold endpoint function pointers */ +static struct usb_ep_ops udc_ep_ops; + +/* received setup data */ +static union udc_setup_data setup_data; + +/* pointer to device object */ +static struct udc *udc; + +/* irq spin lock for soft reset */ +spinlock_t udc_irq_spinlock; +/* stall spin lock */ +spinlock_t udc_stall_spinlock; + +/* this is used for dma chaining */ +static int udc_gfp_flags = 0; + +/* slave mode: pending bytes in rx fifo after nyet, +used if EPIN irq came but no req was available */ +static unsigned int udc_rxfifo_pending = 0; + +/* count soft resets after suspend to avoid loop */ +static int soft_reset_occured = 0; +#ifdef UDC_IPBUG_3958_WORKAROUND +static int soft_reset_after_usbreset_occured = 0; +#endif + +#ifdef UDC_USE_TIMER +/* timer */ +static struct timer_list udc_timer; +static int stop_timer = 0; +int set_rde = -1; +DECLARE_COMPLETION(on_exit); +static struct timer_list udc_pollstall_timer; +static int stop_pollstall_timer = 0; +DECLARE_COMPLETION(on_pollstall_exit); +#endif + +/* tasklet for usb disconnect */ +DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect, (unsigned long) &udc); + +#ifdef USE_AU1200_PCI_DUMMY +static struct pci_dev pdev_dummy; +static struct pci_dev* pdev = &pdev_dummy; +#endif + +#ifdef UDC_IPBUG_3958_WORKAROUND +/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */ +static u32 cnak_pending = 0; +#define UDC_QUEUE_CNAK(ep, num) \ + if (readl(&((ep)->regs->ctl)) & AMD_BIT(UDC_EPCTL_NAK)) { \ + DBG("NAK could not be cleared for ep%d\n", num); \ + cnak_pending |= 1 << (num); \ + (ep)->naking = 1; \ + } \ + else \ + cnak_pending = cnak_pending & (~(1<<(num))); +#else +#define UDC_QUEUE_CNAK(ep, num) {} +#endif + +#ifdef UDC_IPBUG_3943_WORKAROUND +/* otg registering count */ +static u32 otg_reg_count = 0; +#endif +/* gadget registering count */ +static u32 gadget_bind_count = 0; + +/* endpoint names used for print */ +static const char ep0_string[] = "ep0in"; +static const char *ep_string[] = { + ep0_string, + "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk", "ep6in-bulk", "ep7in-bulk", "ep8in-bulk", + "ep9in-bulk", "ep10in-bulk", "ep11in-bulk", "ep12in-bulk", "ep13in-bulk", "ep14in-bulk", "ep15in-bulk", "ep0out", + "ep1out-bulk", "ep2out-bulk", "ep3out-bulk", "ep4out-bulk", "ep5out-bulk", "ep6out-bulk", "ep7out-bulk", "ep8out-bulk", + "ep9out-bulk", "ep10out-bulk", "ep11out-bulk", "ep12out-bulk", "ep13out-bulk", "ep14out-bulk", "ep15out-bulk" +}; + +#ifdef UDC_DEBUG +/* driver callback functions */ +static struct file_operations udc_fops = { + owner: THIS_MODULE, + + read: NULL, + write: NULL, + ioctl: udc_ioctl, + open: udc_open, + release: udc_release, +}; +#endif + +/* PCI device parameters */ +static struct pci_device_id pci_id[] = {{ + .vendor = UDC_PCI_VENID, + .device = UDC_PCI_DEVID, + .class = UDC_PCI_CLASS, + .class_mask = UDC_PCI_CLASS_MASK, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}, +{} +}; + +#if !defined(CONFIG_SOC_AU1200) +/* PCI functions */ +static struct pci_driver udc_pci_driver = { + .name = (char *) name, + .id_table = pci_id, + .probe = udc_pci_probe, + .remove = udc_pci_remove, +}; +#endif + +#ifdef UDC_DEBUG +/* data for debuging only */ +static unsigned long no_pref_req = 0; +static unsigned long no_req = 0; +static u32 same_cfg = 0; +static u32 num_enums = 0; +#endif + +/****** following flags can be set by module parameters */ +/* DMA usage flag */ +static int use_dma = 1; +/* packet per buffer dma */ +static int use_dma_ppb = 1; +/* with per descr. update */ +static int use_dma_ppb_du = 0; +/* buffer fill mode */ +static int use_dma_bufferfill_mode = 0; +/* full speed only mode */ +static int use_fullspeed = 0; +/* tx buffer size for high speed */ +static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE; + +/* module parameters */ +MODULE_PARM (use_dma, "i"); +MODULE_PARM_DESC (use_dma, "true for DMA"); +MODULE_PARM (use_dma_ppb, "i"); +MODULE_PARM_DESC (use_dma_ppb, "true for DMA in packet per buffer mode"); +MODULE_PARM (use_dma_ppb_du, "i"); +MODULE_PARM_DESC (use_dma_ppb_du, "true for DMA in packet per buffer mode with descriptor update"); +MODULE_PARM (use_fullspeed, "i"); +MODULE_PARM_DESC (use_fullspeed, "true for fullspeed only"); +MODULE_PARM (hs_tx_buf, "l"); +MODULE_PARM_DESC (hs_tx_buf, "high speed tx buffer size for data endpoints in dwords"); + +MODULE_DESCRIPTION (UDC_MOD_DESCRIPTION); +MODULE_AUTHOR ("Thomas Dahlmann"); +MODULE_LICENSE("GPL"); + +#ifndef USE_AU1200_PCI_DUMMY +MODULE_DEVICE_TABLE (pci, pci_id); +#endif + +/***************************************************************************** + * Function Definitions + *****************************************************************************/ + +/* AU specific */ +#ifdef CONFIG_SOC_AU1200 +void au1200_sync(void) +{ + au1200_sync_delay(); +} +#endif + +#ifdef UDC_DEBUG +/** + * Dumps byte read access + * + * \param a address pointer + * \return read byte + */ +static void print_td(struct udc_data_dma *p) +{ + INFO("td = %08lx: status=%08lx bufptr=%08lx next=%08lx\n", + (unsigned long) p, + (unsigned long) p->status, + (unsigned long) p->bufptr, + (unsigned long) p->next); +} +#endif + +/* printing registers --------------------------------------------------------*/ +/** + * Prints UDC device registers and endpoint irq registers + * + * \param dev pointer to device struct + */ +static void print_regs(struct udc* dev) +{ +#ifndef UDC_IPBUG_3943_WORKAROUND + DBG( "------- Device registers -------\n"); + DBG( "dev config = %08lx\n", (unsigned long) dev->regs->cfg); + DBG( "dev control = %08lx\n", (unsigned long) dev->regs->ctl); + DBG( "dev status = %08lx\n", (unsigned long) dev->regs->sts); + DBG( "\n"); + DBG( "dev int's = %08lx\n", (unsigned long) dev->regs->irqsts); + DBG( "dev intmask = %08lx\n", (unsigned long) dev->regs->irqmsk); + DBG( "\n"); + DBG( "dev ep int's = %08lx\n", (unsigned long) dev->regs->ep_irqsts); + DBG( "dev ep intmask = %08lx\n", (unsigned long) dev->regs->ep_irqmsk); + DBG( "\n"); +#endif + DBG( "USE DMA = %d\n", use_dma); + if (use_dma && use_dma_ppb && !use_dma_ppb_du) { + DBG( "DMA mode = PPBNDU (packet per buffer WITHOUT desc. update)\n"); +#ifndef UDC_DEBUG + INFO( "DMA mode (PPBNDU)\n"); +#endif + } + else if (use_dma && use_dma_ppb_du && use_dma_ppb_du) { + DBG( "DMA mode = PPBDU (packet per buffer WITH desc. update)\n"); +#ifndef UDC_DEBUG + INFO( "DMA mode (PPBDU)\n"); +#endif + } + if (use_dma && use_dma_bufferfill_mode) { + DBG( "DMA mode = BF (buffer fill mode)\n"); +#ifndef UDC_DEBUG + INFO( "DMA mode (BF)\n"); +#endif + } +#ifndef UDC_DEBUG + if (!use_dma) { + INFO( "FIFO mode\n"); + } +#endif +#ifdef UDC_USE_TIMER + INFO("RDE timer is used\n"); +#endif + DBG("-------------------------------------------------------\n"); +} + +/** + * Prints snapshot of ep registers + * + * \param dev pointer to device struct + */ +#ifdef UDC_DEBUG +static void print_ep_regs(struct udc* dev, struct udc_ep_regs* ep_regs) +{ + INFO( "ep control = %08lx\n", (unsigned long) ep_regs->ctl); + INFO( "ep status = %08lx\n", (unsigned long) ep_regs->sts); + INFO( "--------------------------------\n"); +} + +/** + * Prints misc information, to be removed + * + * \param dev pointer to device struct + */ +static void print_misc(struct udc* dev) +{ + print_regs(dev); + + if (use_dma) { + + INFO("no_req=%ld no_pref_req=%ld\n", no_req, no_pref_req); + } +} +#endif + +/* driver functions ----------------------------------------------------------*/ +/** + * Called by OS for insmod + * + * \param inode file node + * \param file struct + * \return read byte + */ +#ifdef UDC_DEBUG +static int udc_open (struct inode *inode, struct file *file) +{ + int retval = 0; + return retval; +} + +/** + * Called by OS for rmmod + * + * \param inode file node + * \param file file struct + * \return read byte + */ +static int udc_release (struct inode *inode, struct file *file) +{ + int retval = 0; + return retval; +} + +/** + * Called by OS for ioctl() from user space + * + * \param inode file node + * \param file file struct + * \param command ioctl command code + * \param argument ioctl argument + * \return 0 if success + */ +static int udc_ioctl (struct inode *inode, + struct file *file, + unsigned int command, + unsigned long argument) +{ + struct udc* dev = udc; + + if (!dev) + return -EINVAL; + + /* for testing PM */ + switch (command) + { + case 1: + udc_remote_wakeup(udc); + break; + case 5: + udc_suspend(udc); + break; + case 6: + udc_resume(udc); + break; + } + + INFO( "ioctl called\n"); + + return 0; +} + +/** + * Prints a buffers contents + * \param buf pointer to buffer + * \param bytes number bytes to print + */ +static void dump_buffer(u8* buf, u32 bytes) +{ + int i; + + printk("\nbuffer %lx = %d bytes:\n", (unsigned long) buf, bytes); + for (i = 0; i < bytes; i++) { + printk("%02x", *(buf + i)); + if ((i+1)%16 == 0) + printk("\n"); + } + printk("\n"); +} +#endif + +/** + * Masks unused interrupts + * + * \param dev pointer to device struct + * \return 0 if success + */ +static int udc_mask_unused_interrupts(struct udc* dev) +{ + u32 tmp; + + /* mask all dev interrupts */ + tmp = AMD_BIT(UDC_DEVINT_SVC) | + AMD_BIT(UDC_DEVINT_ENUM) | + AMD_BIT(UDC_DEVINT_US) | + AMD_BIT(UDC_DEVINT_UR) | + AMD_BIT(UDC_DEVINT_ES) | + AMD_BIT(UDC_DEVINT_SI) | + AMD_BIT(UDC_DEVINT_SOF)| + AMD_BIT(UDC_DEVINT_SC); + writel(tmp, &dev->regs->irqmsk); + + /* mask all ep interrupts */ + writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk); + + return 0; +} + +/** + * Enables endpoint 0 interrupts + * + * \param dev pointer to device struct + * \return 0 if success + */ +static int udc_enable_ep0_interrupts(struct udc* dev) +{ + u32 tmp; + + DBG("udc_enable_ep0_interrupts()\n"); + + /* read irq mask */ + tmp = readl(&dev->regs->ep_irqmsk); + /* enable ep0 irq's */ + tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0) + & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0); + writel(tmp, &dev->regs->ep_irqmsk); + + return 0; +} + +/** + * Enables device interrupts for SET_INTF and SET_CONFIG + * + * \param dev pointer to device struct + * \return 0 if success + */ +static int udc_enable_dev_setup_interrupts(struct udc* dev) +{ + u32 tmp; + + DBG("enable device interrupts for setup data\n"); + + /* read irq mask */ + tmp = readl(&dev->regs->irqmsk); + + /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */ + tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI) + & AMD_UNMASK_BIT(UDC_DEVINT_SC) + & AMD_UNMASK_BIT(UDC_DEVINT_UR) +#ifndef UDC_IPBUG_3943_WORKAROUND + & AMD_UNMASK_BIT(UDC_DEVINT_US) +#endif +#ifndef UDC_IPBUG_3950_WORKAROUND + & AMD_UNMASK_BIT(UDC_DEVINT_SVC) +#endif + & AMD_UNMASK_BIT(UDC_DEVINT_ENUM); + writel(tmp, &dev->regs->irqmsk); + + return 0; +} + +/** + * Calculates fifo start of endpoint based on preceeding endpoints + * + * \param ep pointer to ep struct + * \return 0 if success + */ +static int udc_set_txfifo_addr(struct udc_ep *ep) +{ + struct udc *dev; + u32 tmp; + int i; + + if (!ep || !(ep->in)) + return -EINVAL; + + dev = ep->dev; + ep->txfifo = dev->txfifo; + + /* traverse ep's */ + for (i = 0; i < ep->num; i++) { + if (dev->ep[i].regs) { + /* read fifo size */ + tmp = readl(&dev->ep[i].regs->bufin_framenum); + tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE); + ep->txfifo+= tmp; + } + } + return 0; +} + +/** + * Enables endpoint, is called by gadget driver + * + * \param usbep pointer to ep struct + * \param desc pointer to endpoint descriptor + * \return 0 if success + */ +static int +udc_enable (struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) +{ + struct udc_ep *ep; + struct udc *dev; + u32 tmp; + unsigned long iflags; + u8 udc_csr_epix; + + VDBG("udc_enable()\n"); + + ep = container_of (usbep, struct udc_ep, ep); + if (!usbep + || usbep->name == ep0_string + || !desc + || desc->bDescriptorType != USB_DT_ENDPOINT) { + ERR("udc_enable: !usbep=%d !desc=%d ep->desc!=NULL=%d usbep->name==ep0_string=%d desc->bDescriptorType!=USB_DT_ENDPOINT=%d\n", + !usbep, !desc, ep->desc != NULL, usbep->name == ep0_string, desc->bDescriptorType != USB_DT_ENDPOINT); + return -EINVAL; + } + + DBG("udc_enable() ep %d\n", ep->num); + + dev = ep->dev; + + /* exit on suspend */ + if (dev->sys_suspended) + return -ESHUTDOWN; + + if (!dev->driver + || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave (&dev->lock, iflags); + ep->desc = desc; + + ep->halted = 0; + + /* set traffic type */ + tmp = readl(&dev->ep[ep->num].regs->ctl); + tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET); + writel(tmp, &dev->ep[ep->num].regs->ctl); + + /* set max packet size */ + tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); + tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_EP_MAX_PKT_SIZE); + ep->ep.maxpacket = desc->wMaxPacketSize; + writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt); + + /* IN ep */ + if (ep->in) { + + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num; + + /* set buffer size (tx fifo entries) */ + tmp = readl(&dev->ep[ep->num].regs->bufin_framenum); + /* double buffering: fifo size = 2 x max packet size */ + tmp = AMD_ADDBITS( + tmp, + desc->wMaxPacketSize * UDC_EPIN_BUFF_SIZE_MULT / + UDC_DWORD_BYTES, + UDC_EPIN_BUFF_SIZE); + writel(tmp, &dev->ep[ep->num].regs->bufin_framenum); + + /* calc. tx fifo base addr */ + udc_set_txfifo_addr(ep); + + /* flush fifo */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_F); + writel(tmp, &ep->regs->ctl); + + } /* OUT ep */ + else { + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; + + /* set max packet size UDC CSR */ + tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]); + tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT); + writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]); + + if (ep->num != UDC_EP0OUT_IX) + dev->data_ep_enabled = 1; + } + + /***** UDC CSR reg ****************************/ + /* set ep values */ + tmp = readl(&dev->csr->ne[udc_csr_epix]); + /* max packet */ + tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT); + /* ep number */ + tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM); + /* ep direction */ + tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR); + /* ep type */ + tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE); + /* ep config */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG); + /* ep interface */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF); + /* ep alt */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT); + /* write reg */ + writel(tmp, &dev->csr->ne[udc_csr_epix]); + + /* enable ep irq */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp &= AMD_UNMASK_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + + /* clear NAK by writing CNAK */ + /* avoid BNA for DMA, dont clear NAK until DMA desc. written */ + if (!use_dma) { + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + tmp = desc->bEndpointAddress; + DBG( "%s enabled\n", + usbep->name); + + spin_unlock_irqrestore (&dev->lock, iflags); + return 0; +} +/** + * Resets endpoint + * + * \param regs pointer to device register struct + * \param ep pointer to endpoint + */ +static void ep_init (struct udc_regs *regs, struct udc_ep *ep) +{ + u32 tmp; + + VDBG("ep-%d reset\n", ep->num); + ep->desc = 0; + ep->ep.ops = &udc_ep_ops; + INIT_LIST_HEAD (&ep->queue); + + ep->ep.maxpacket = (u16) ~0; + if (!(ep->dev->sys_suspended)) { + /* set NAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 1; + + /* disable interrupt */ + tmp = readl(®s->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, ®s->ep_irqmsk); + + if (ep->in) { + /* unset P and IN bit of potential former DMA */ + tmp = readl(&ep->regs->ctl); + tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P); + writel(tmp, &ep->regs->ctl); + + tmp = readl(&ep->regs->sts); + tmp |= AMD_BIT(UDC_EPSTS_IN); + writel(tmp, &ep->regs->sts); + + /* flush the fifo */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_F); + writel(tmp, &ep->regs->ctl); + + } + /* reset desc pointer */ + writel(0, &ep->regs->desptr); + } + + +} + +/** + * Disables endpoint, is called by gadget driver + * + * \param usbep pointer to ep struct + * \return 0 if success + */ +static int udc_disable (struct usb_ep *usbep) +{ + struct udc_ep *ep = NULL; + unsigned long iflags; + + if (!usbep) + return -EINVAL; + + ep = container_of (usbep, struct udc_ep, ep); + if (usbep->name == ep0_string + || !ep->desc) + return -EINVAL; + + DBG("Disable ep-%d\n", ep->num); + + spin_lock_irqsave(&ep->dev->lock, iflags); + empty_req_queue(ep); + ep_init(ep->dev->regs, ep); + spin_unlock_irqrestore(&ep->dev->lock, iflags); + + return 0; +} + +/** + * Allocates request packet, called by gadget driver + * + * \param _ep pointer to usb ep struct + * \param gfp_flags flags for kmalloc + * \return allocated request packet, 0 if error + */ +static struct usb_request * +udc_alloc_request (struct usb_ep *usbep, gfp_t gfp) +{ + struct udc_request *req; + struct udc_data_dma *dma_desc; + struct udc_ep *ep; + + VDBG("udc_alloc_req()\n"); + if (!usbep) + return 0; + + ep = container_of (usbep, struct udc_ep, ep); + udc_gfp_flags = gfp; + + VDBG("udc_alloc_req(): ep%d\n", ep->num); + req = kmalloc (sizeof (struct udc_request), gfp); + if (!req) + return 0; + + memset (req, 0, sizeof *req); + req->req.dma = DMA_DONT_USE; + INIT_LIST_HEAD (&req->queue); + + if (ep->dma) { + +#if defined(CONFIG_SOC_AU1200) + gfp = GFP_ATOMIC | GFP_DMA; +#endif + /* ep0 in requests are allocated from data pool here */ + dma_desc = dma_pool_alloc (ep->dev->data_requests, gfp, + &req->td_phys); + if (!dma_desc) { + kfree (req); + return 0; + } + + VDBG("udc_alloc_req: req = %lx dma_desc = %lx, req->td_phys = %lx\n", + (unsigned long) req, (unsigned long) dma_desc, (unsigned long)req->td_phys); + /* prevent from using desc. - set HOST BUSY */ + dma_desc->status = AMD_ADDBITS(dma_desc->status, + UDC_DMA_STP_STS_BS_HOST_BUSY, + UDC_DMA_STP_STS_BS); + dma_desc->bufptr = __constant_cpu_to_le32 (DMA_DONT_USE); + req->td_data = dma_desc; + req->td_data_last = NULL; + req->chain_len = 1; + } + + return &req->req; +} + +/** + * Frees request packet, called by gadget driver + * + * \param usbep pointer to usb ep struct + * \param usbreq pointer to request packet to be freed + */ +static void +udc_free_request (struct usb_ep *usbep, struct usb_request *usbreq) +{ + struct udc_ep *ep; + struct udc_request *req; + + if (!usbep || !usbreq) + return; + + ep = container_of (usbep, struct udc_ep, ep); + req = container_of (usbreq, struct udc_request, req); + VDBG("free_req req=%lx\n", (unsigned long) req); + WARN_ON(!list_empty (&req->queue)); + if (req->td_data) { + VDBG("req->td_data=%lx\n", (unsigned long) req->td_data); + +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + /* re-link broken chain */ + if (req->td_data_last) { + req->td_data_last->next = req->td_data_last_next; + } +#endif + /* free dma chain if created */ + if (req->chain_len > 1) { + udc_free_dma_chain(ep->dev, req); + } + + dma_pool_free (ep->dev->data_requests, req->td_data, req->td_phys); + } + kfree (req); +} + +/* choose dma buffer allocation method */ +#undef USE_KMALLOC +#if defined(CONFIG_X86) +#define USE_KMALLOC +#elif defined(CONFIG_SOC_AU1200) +#ifdef CONFIG_DMA_COHERENT +#define USE_KMALLOC +#endif /* CONFIG_DMA_COHERENT */ +#endif + +/** + * Allocates data buffer for request packet, called by gadget driver + * + * \param usbep pointer to usb ep struct + * \param bytes number bytes to allocate + * \param dma pointer to dma struct + * \param gfp_flags flags for allocate + * \return pointer to allocated buffer, 0 if error + */ +static void * +udc_alloc_buffer ( + struct usb_ep *usbep, + unsigned len, + dma_addr_t *dma, + gfp_t gfp +) +{ + void *ptr; + struct udc_ep *ep; + VDBG("alloc_buffer(): %s\n", usbep->name); + + ep = container_of (usbep, struct udc_ep, ep); + *dma = DMA_DONT_USE; + + if (!usbep) + return 0; + + if (ep->dma) { +#if defined(USE_KMALLOC) + +#if defined(CONFIG_SOC_AU1200) + gfp = GFP_ATOMIC | GFP_DMA; +#endif + ptr = kmalloc(len, gfp); + + if (ptr) + *dma = virt_to_phys(ptr); + +#else + ptr = dma_alloc_coherent (&ep->dev->pdev->dev, len, dma, gfp); +#endif + } else + ptr = kmalloc(len, gfp); + + VDBG("alloc_buffer() ptr = %lx dma=%lx\n", (unsigned long) ptr, (unsigned long) *dma); + return ptr; +} + + +/** + * Free data buffer for request packet, called by gadget driver + * + * \param usbep pointer to usb ep struct + * \param buf pointer to buffer to be freed + * \param dma pointer to dma struct + * \param len number bytes to be freed + * \return pointer to allocated buffer, 0 if error + */ +static void +udc_free_buffer ( + struct usb_ep *usbep, + void *buf, + dma_addr_t dma, + unsigned len +) { + struct udc_ep *ep; + + VDBG ("ep %s - free buffer %lx\n", usbep->name, (unsigned long) buf); + + /* null pointer ? */ + if (!buf) { + return; + } + + ep = container_of(usbep, struct udc_ep, ep); + if (!ep) + return; + +#ifndef USE_KMALLOC + if (dma != DMA_DONT_USE) { + dma_free_coherent (&ep->dev->pdev->dev, len, buf, dma); + } else +#endif + { + if (!(ep->dma)) + kfree (buf); + } + +} + +/** + * Write data to TX fifo for IN packets + * + * \param ep pointer to ep struct + * \param req pointer to request packet + * \return allocated request packet, 0 if error + */ +static void +udc_txfifo_write (struct udc_ep *ep, struct usb_request *req) +{ + u8 *req_buf; + u32 *buf; + int i,j; + unsigned bytes = 0; + unsigned remaining = 0; + + VDBG("udc_txfifo_write()\n"); + + if (!req || !ep) + return; + + req_buf = req->buf + req->actual; + prefetch (req_buf); + remaining = req->length - req->actual; + + buf = (u32*) req_buf; + + bytes = ep->ep.maxpacket; + if (bytes > remaining) + bytes = remaining; + + /* dwords first */ + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + writel(*(buf + i), ep->txfifo); + } + + /* remaining bytes must be written by byte access */ + for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) { + writeb((u8) (*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)), + ep->txfifo); + } + +#ifdef UDC_IPBUG_2253_WORKAROUND + { + u32 tmp; + + /* NAK if small packet until write confirm completed */ + if (bytes < UDC_SMALL_PACKET) { + /* set NAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 1; + wb_flush(); + } + + /* dummy write confirm */ + writel(0, &ep->regs->confirm); + + /* stop NAKing after small packet DMA */ + if (ep->naking) { + /* clear NAK by writing CNAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + } +#else + /* dummy write confirm */ + writel(0, &ep->regs->confirm); +#endif +} + +/** + * Read dwords from RX fifo for OUT transfers + * + * \param dev pointer to device struct + * \param buf pointer to buffer to be filled + * \param dwords number of dwords to be read + * \return allocated request packet, 0 if error + */ +static int udc_rxfifo_read_dwords(struct udc* dev, u32* buf, int dwords) +{ + int i; + + VDBG("udc_read_dwords(): %d dwords\n", dwords); + + for (i = 0; i < dwords; i++) + { + *(buf + i) = readl(dev->rxfifo); + } + return 0; +} + +/** + * Read bytes from RX fifo for OUT transfers + * + * \param dev pointer to device struct + * \param buf pointer to buffer to be filled + * \param bytes number of bytes to be read + * \return allocated request packet, 0 if error + */ +static int udc_rxfifo_read_bytes(struct udc* dev, u8* buf, int bytes) +{ + int i,j; + u32 tmp; + + VDBG("udc_read_bytes(): %d bytes\n", bytes); + + /* dwords first */ + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + *((u32*) (buf + (i<<2))) = readl(dev->rxfifo); + } + + /* remaining bytes must be read by byte access */ + if (bytes % UDC_DWORD_BYTES) { + tmp = readl(dev->rxfifo); + for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) { + *(buf + (i<<2) + j) = (u8) (tmp & UDC_BYTE_MASK); + tmp = tmp >> UDC_BITS_PER_BYTE; + } + } + + return 0; +} + +/** + * Read data from RX fifo for OUT transfers + * + * \param ep pointer to ep struct + * \param req pointer to request packet + * \return true if request completes for short or max packet, false otherwise + */ +static int +udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req) +{ + u8 *buf; + unsigned buf_space; + unsigned bytes = 0; + unsigned finished = 0; + + /* received number bytes */ + bytes = readl(&ep->regs->sts); + bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE); + + buf_space = req->req.length - req->req.actual; + buf = req->req.buf + req->req.actual; + if (bytes > buf_space) { + if ((buf_space % ep->ep.maxpacket) != 0) { + ERR( "%s: received %d bytes, rx-buffer space = %d bytes => buffer overrun\n", + ep->ep.name, bytes, buf_space); + req->req.status = -EOVERFLOW; + } + bytes = buf_space; + } + req->req.actual += bytes; + + /* last packet ? */ + if (((bytes % ep->ep.maxpacket) != 0) + || (!bytes) + || ((req->req.actual == req->req.length) && !req->req.zero)) + finished = 1; + + /* read rx fifo bytes */ + VDBG("ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes); + udc_rxfifo_read_bytes(ep->dev, buf, bytes); + + return finished; +} + +/** + * create/re-init a DMA descriptor or a DMA descriptor chain + * + * \param ep pointer to endpoint struct + * \param req pointer to request packet + */ +static int prep_dma (struct udc_ep *ep, struct udc_request *req) +{ + int retval = 0; + u32 tmp; + VDBG("prep_dma\n"); + VDBG("prep_dma ep%d req->td_data=%lx\n", + ep->num, (unsigned long) req->td_data); + + /* set buffer pointer */ + req->td_data->bufptr = req->req.dma; + + /* set last bit */ + req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L); + + /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */ + if (use_dma_ppb && ep->num != UDC_EP0OUT_IX && ep->num != UDC_EP0IN_IX) { + + retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, udc_gfp_flags); + if (retval != 0) + { + if (retval == -ENOMEM) + INFO("Out of DMA memory (allocation failed)\n"); + return retval; + } + if (ep->in) { + if (req->req.length == ep->ep.maxpacket) { + /* write tx bytes */ + req->td_data->status = AMD_ADDBITS(req->td_data->status, + ep->ep.maxpacket, + UDC_DMA_IN_STS_TXBYTES); + + } + } + + } +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + else { /* EP0 */ + /* point to itself */ + req->td_data->next = req->td_phys; + } +#endif + + if (ep->in) { + VDBG("IN: use_dma_ppb=%d req->req.length=%d ep->ep.maxpacket=%d ep%d\n", + use_dma_ppb, req->req.length, ep->ep.maxpacket, ep->num); + /* if bytes < max packet then tx bytes must */ + /* be written in packet per buffer mode */ + if (!use_dma_ppb || req->req.length < ep->ep.maxpacket + || ep->num == UDC_EP0OUT_IX || ep->num == UDC_EP0IN_IX) { + /* write tx bytes */ + req->td_data->status = AMD_ADDBITS(req->td_data->status, + req->req.length, + UDC_DMA_IN_STS_TXBYTES); + /* reset frame num */ + req->td_data->status = AMD_ADDBITS(req->td_data->status, + 0, + UDC_DMA_IN_STS_FRAMENUM); + } + /* set HOST BUSY */ + req->td_data->status + = AMD_ADDBITS(req->td_data->status, + UDC_DMA_STP_STS_BS_HOST_BUSY, + UDC_DMA_STP_STS_BS); + } + else { + VDBG("OUT set host ready\n"); + /* set HOST READY */ + req->td_data->status + = AMD_ADDBITS(req->td_data->status, + UDC_DMA_STP_STS_BS_HOST_READY, + UDC_DMA_STP_STS_BS); + + + /* clear NAK by writing CNAK */ + if (ep->naking) { + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + + } + + return retval; +} + +/** + * Completes request packet + * + * \param ep pointer to ep struct + * \param req pointer to request packet + * \param sts status of request + */ +static void +complete_req(struct udc_ep *ep, struct udc_request *req, int sts) +{ + struct udc *dev; + unsigned halted; + + VDBG("complete_req(): ep%d\n", ep->num); + + dev = ep->dev; + /* unmap DMA */ + if (req->dma_mapping) { +#if defined(CONFIG_SOC_AU1200) + if (ep->in) + dma_unmap_single(0, + req->req.dma, + req->req.length, + PCI_DMA_TODEVICE); + else + dma_unmap_single(0, + req->req.dma, + req->req.length, + PCI_DMA_FROMDEVICE); +#else + 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); +#endif + req->dma_mapping = 0; + req->req.dma = DMA_DONT_USE; + } + + halted = ep->halted; + ep->halted = 1; + + /* set new status if pending */ + if (req->req.status == -EINPROGRESS) + req->req.status = sts; + + /* remove from ep queue */ + list_del_init (&req->queue); + + VDBG( "req %p => complete %d bytes at %s with sts %d\n", + &req->req, req->req.length, ep->ep.name, sts); + if (spin_is_locked(&dev->lock)) { + spin_unlock (&dev->lock); + req->req.complete (&ep->ep, &req->req); + spin_lock (&dev->lock); + } + else { + req->req.complete (&ep->ep, &req->req); + } + ep->halted = halted; +} + +/** + * frees pci pool descriptors of a DMA chain + * + * \param dev pointer to device struct + * \param req pointer to request packet + * \return 0 if success + */ +static int udc_free_dma_chain(struct udc* dev, struct udc_request* req) +{ + + int ret_val = 0; + struct udc_data_dma *td; + struct udc_data_dma *td_last = NULL; + unsigned int i; + + DBG("free chain req = %lx\n", (unsigned long) req); + +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + /* re-link broken chain */ + if (req->td_data_last) { + req->td_data_last->next = req->td_data_last_next; + } +#endif + /* do not free first desc., will be done by free for request */ + td_last = req->td_data; +#if defined (CONFIG_SOC_AU1200) && !defined(CONFIG_DMA_COHERENT) + td = UNCAC_ADDR(phys_to_virt(td_last->next)); +#else + td = phys_to_virt(td_last->next); +#endif + + for (i = 1; i < req->chain_len; i++) { + + dma_pool_free (dev->data_requests, td, + (dma_addr_t) td_last->next); + td_last = td; +#if defined (CONFIG_SOC_AU1200) && !defined(CONFIG_DMA_COHERENT) + td = UNCAC_ADDR(phys_to_virt(td_last->next)); +#else + td = phys_to_virt(td_last->next); +#endif + } + +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + req->td_data_last = NULL; +#endif + return ret_val; +} + +/** + * Iterates to the end of a DMA chain and returns last descriptor + * + * \param req pointer to request packet + * \return pointer to last descriptori of chain + */ +static struct udc_data_dma* udc_get_last_dma_desc(struct udc_request* req) +{ + struct udc_data_dma *td; + + td = req->td_data; + while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) { +#if defined (CONFIG_SOC_AU1200) && !defined(CONFIG_DMA_COHERENT) + td = UNCAC_ADDR(phys_to_virt(td->next)); +#else + td = phys_to_virt(td->next); +#endif + } + + return td; + +} + +/** + * Iterates to the end of a DMA chain and counts bytes received + * + * \param req pointer to request packet + * \return number of received bytes + */ +static u32 udc_get_ppbdu_rxbytes(struct udc_request* req) +{ + struct udc_data_dma *td; + u32 count; + + td = req->td_data; + /* received number bytes */ + count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES); + + while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) { +#if defined (CONFIG_SOC_AU1200) && !defined(CONFIG_DMA_COHERENT) + td = UNCAC_ADDR(phys_to_virt(td->next)); +#else + td = phys_to_virt(td->next); +#endif + /* received number bytes */ + if (td) { + count += AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES); + } + } + + return count; + +} + +/** + * Creates or re-inits a DMA chain + * + * \param ep pointer to endpoint struct + * \param req pointer to request packet + * \param buf_len number of buffer bytes per descriptor (except last short) + */ +static int udc_create_dma_chain(struct udc_ep* ep, struct udc_request* req, unsigned long buf_len, int gfp_flags) +{ + unsigned long bytes = req->req.length; + unsigned int i; + dma_addr_t dma_addr; + struct udc_data_dma *td = NULL; + struct udc_data_dma *last = NULL; + unsigned long txbytes; + unsigned create_new_chain = 0; + unsigned len; +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + dma_addr_t last_next = DMA_DONT_USE; +#endif + + VDBG("udc_create_dma_chain: bytes=%ld buf_len=%ld\n", bytes, buf_len); + dma_addr = DMA_DONT_USE; + + /* unset L bit in first desc for OUT */ + if (!ep->in) { + req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); + } + + /* alloc only new desc's if not already available */ + len = req->req.length / ep->ep.maxpacket; + if (req->req.length % ep->ep.maxpacket) { + len++; + } + + if (len > req->chain_len) { + /* shorter chain already allocated before */ + if (req->chain_len > 1) { + udc_free_dma_chain(ep->dev, req); + } + req->chain_len = len; + create_new_chain = 1; + } + +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + /* re-link broken chain */ + if (req->td_data_last) { + req->td_data_last->next = req->td_data_last_next; + } + /* if only one td then last_next is root td */ + last_next = req->td_phys; +#endif + + td = req->td_data; + /* gen. required number of descriptors and buffers */ + for (i = buf_len; i < bytes; i += buf_len) + { + /* create or determine next desc. */ + if (create_new_chain) { + +#if defined(CONFIG_SOC_AU1200) + gfp_flags = GFP_ATOMIC | GFP_DMA; +#endif + td = dma_pool_alloc (ep->dev->data_requests, gfp_flags, + &dma_addr); + if (!td) + return -ENOMEM; + + td->status = 0; + } + else if (i == buf_len) + { + /* first td */ +#if defined (CONFIG_SOC_AU1200) && !defined(CONFIG_DMA_COHERENT) + td = (struct udc_data_dma*) UNCAC_ADDR(phys_to_virt(req->td_data->next)); +#else + td = (struct udc_data_dma*) phys_to_virt(req->td_data->next); +#endif + td->status = 0; + } + else { +#if defined (CONFIG_SOC_AU1200) && !defined(CONFIG_DMA_COHERENT) + td = (struct udc_data_dma*) UNCAC_ADDR(phys_to_virt(last->next)); +#else + td = (struct udc_data_dma*) phys_to_virt(last->next); +#endif + td->status = 0; + } + + + if (td) { + /* assign buffer */ + td->bufptr = req->req.dma + i; + } + else { + break; + } + + /* short packet ? */ + if ((bytes - i) >= buf_len) { + txbytes = buf_len; + } + else { + /* short packet */ + txbytes = bytes - i; + } + + /* link td and assign tx bytes */ + if (i == buf_len) { + if (create_new_chain) { + req->td_data->next = dma_addr; + } + else { + /* req->td_data->next = virt_to_phys(td); */ + } + /* write tx bytes */ + if (ep->in) { + /* first desc */ + req->td_data->status = AMD_ADDBITS(req->td_data->status, + ep->ep.maxpacket, + UDC_DMA_IN_STS_TXBYTES); + /* second desc */ + td->status = AMD_ADDBITS(td->status, + txbytes, + UDC_DMA_IN_STS_TXBYTES); + } + +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + last_next = req->td_data->next; +#endif + } + else { + if (create_new_chain) { + last->next = dma_addr; + } + else { + /* last->next = virt_to_phys(td); */ + } + if (ep->in) { + /* write tx bytes */ + td->status = AMD_ADDBITS(td->status, + txbytes, + UDC_DMA_IN_STS_TXBYTES); + } +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + last_next = last->next; +#endif + } + last = td; + } + /* set last bit */ + if (td) { + td->status |= AMD_BIT(UDC_DMA_IN_STS_L); + /* last desc. points to itself */ +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + /* remember broken chain next pointer */ + req->td_data_last_next = td->next; + /* point to itself */ + td->next = last_next; +#endif + req->td_data_last = td; + } + + return 0; +} + +/** + * Enabling RX DMA + * + * \param dev pointer to UDC device object + */ +static inline void udc_set_rde(struct udc* dev) +{ + u32 tmp; + + VDBG("udc_set_rde()\n"); +#ifdef UDC_USE_TIMER + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } +#endif + /* set RDE */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RDE); + writel(tmp, &dev->regs->ctl); +} + +/** + * Queues a request packet, called by gadget driver + * + * \param usbep pointer to usb ep struct + * \param usbreq pointer to request packet to be freed + * \param gfp flags for alloc + * \return 0 if success + */ +static int +udc_queue (struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) +{ + int retval = 0; + unsigned long iflags; + struct udc_ep *ep; + struct udc_request *req; + struct udc *dev; + u32 tmp; + + VDBG ("udc_queue()\n"); + + /* check the inputs */ + req = container_of (usbreq, struct udc_request, req); + VDBG("!usbep=%d !req=%d !buf=%d !compl=%d !empty_list=%d \n", + !usbep, !usbreq, !usbreq->buf, !usbreq->complete, + !list_empty(&req->queue)); + + if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf + || !list_empty (&req->queue)) + return -EINVAL; + + ep = container_of (usbep, struct udc_ep, ep); + if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) + return -EINVAL; + + VDBG("udc_queue(): ep%d-in=%d\n", ep->num, ep->in); + dev = ep->dev; + + /* exit on suspend */ + if (dev->sys_suspended) + return -ESHUTDOWN; + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* map dma (usally done before) */ + if (ep->dma && usbreq->length != 0 && usbreq->dma == DMA_DONT_USE) { + VDBG("DMA map req %lx\n", (unsigned long) req); +#if defined(CONFIG_SOC_AU1200) + if (ep->in) + usbreq->dma = dma_map_single(dev->pdev, + usbreq->buf, + usbreq->length, + PCI_DMA_TODEVICE); + else + usbreq->dma = dma_map_single(dev->pdev, + usbreq->buf, + usbreq->length, + PCI_DMA_FROMDEVICE); +#else + 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); +#endif + req->dma_mapping = 1; + } + + VDBG( "%s queue req %p, len %d req->td_data=%lx buf %p\n", + usbep->name, usbreq, usbreq->length, (unsigned long) req->td_data, usbreq->buf); + + spin_lock_irqsave (&dev->lock, iflags); + usbreq->actual = 0; + usbreq->status = -EINPROGRESS; + req->dma_done = 0; + + /* on empty queue just do first transfer */ + if (list_empty (&ep->queue)) { + /* zlp */ + if (ep->in && usbreq->length == 0) { + /* IN zlp's are handled by hardware */ + complete_req(ep, req, 0); + VDBG( "%s: zlp\n", ep->ep.name); + /* if set_config or set_intf is waiting for ack by zlp + then set CSR_DONE */ + if (dev->set_cfg_not_acked) { + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE); + writel(tmp, &dev->regs->ctl); + dev->set_cfg_not_acked = 0; + } + /* setup command is ACK'ed now by zlp */ + if (dev->waiting_zlp_ack_ep0in) { + /* clear NAK by writing CNAK in EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX); + dev->waiting_zlp_ack_ep0in = 0; + } + goto finished; + } + if (ep->dma) { + retval = prep_dma(ep, req); + if (retval != 0) + goto finished; + /* write desc pointer to enable DMA */ + if (ep->in) { + /* set HOST READY */ + req->td_data->status + = AMD_ADDBITS(req->td_data->status, + UDC_DMA_IN_STS_BS_HOST_READY, + UDC_DMA_IN_STS_BS); + } + + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + /* clear NAK by writing CNAK */ + if (ep->naking) { + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + + if (!ep->in) { + /* enable DMA */ + udc_set_rde(dev); + if (ep->num != UDC_EP0OUT_IX) + dev->data_ep_queued = 1; + } +#ifdef UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE + else { + /* enable ep irq */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp &= AMD_UNMASK_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + } +#endif + } + + } else if (ep->dma) { + + /* prep_dma not used for OUT ep's, this is not possible + for PPB modes, because of chain creation reasons */ + if (ep->in) { + retval = prep_dma(ep, req); + if (retval != 0) + goto finished; + } + + } + VDBG("list_add\n"); + /* add request to ep queue */ + if (req) { + + list_add_tail (&req->queue, &ep->queue); + + /* stop OUT naking */ + if (!ep->in) { + if (!use_dma && udc_rxfifo_pending) { + DBG("udc_queue(): pending bytes in rxfifo after nyet\n"); + /* read pending bytes afer nyet: referring to isr */ + if (udc_rxfifo_read(ep, req)) { + /* finish */ + complete_req(ep, req, 0); + } + udc_rxfifo_pending = 0; + + } + } + + } + +finished: + spin_unlock_irqrestore (&dev->lock, iflags); + return retval; +} + +/** + * Empty request queue of an endpoint + * + * \param ep pointer to ep struct + */ +static void empty_req_queue(struct udc_ep *ep) +{ + struct udc_request *req; + + ep->halted = 1; + while (!list_empty (&ep->queue)) { + req = list_entry(ep->queue.next, + struct udc_request, + queue); + complete_req(ep, req, -ESHUTDOWN); + } +} + +/** + * Dequeues a request packet, called by gadget driver + * + * \param usbep pointer to usb ep struct + * \param usbreq pointer to request packet to be freed + * \return 0 if success + */ +static int udc_dequeue (struct usb_ep *usbep, struct usb_request *usbreq) +{ + struct udc_ep *ep; + struct udc_request *req; + unsigned req_may_used = 0; + unsigned halted; + unsigned long iflags; + + ep = container_of (usbep, struct udc_ep, ep); + if (!usbep + || !usbreq + || (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))) + return -EINVAL; + + req = container_of (usbreq, struct udc_request, req); + + spin_lock_irqsave (&ep->dev->lock, iflags); + halted = ep->halted; + ep->halted = 1; + /* request in processing or next one */ + if (ep->queue.next == &req->queue) { + req_may_used = 1; + } + complete_req(ep, req, -ECONNRESET); + ep->halted = halted; + + spin_unlock_irqrestore (&ep->dev->lock, iflags); + if (req_may_used) + return -EOPNOTSUPP; + else + return 0; +} + +/** + * Halt or clear halt of endpoint + * + * \param usbe pointer to ep struct + * \param halt 1 - halt, 0 - clear halt + * \return 0 if success + */ +static int +udc_set_halt (struct usb_ep *usbep, int halt) +{ + struct udc_ep *ep; + u32 tmp; + unsigned long iflags; + int retval = 0; + + if (!usbep) + return -EINVAL; + + DBG("set_halt %s: halt=%d\n", usbep->name, halt); + + ep = container_of (usbep, struct udc_ep, ep); + if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) + return -EINVAL; + if (!ep->dev->driver + || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* exit on suspend */ + if (ep->dev->sys_suspended) + return -ESHUTDOWN; + + spin_lock_irqsave (&udc_stall_spinlock, iflags); + /* halt or clear halt */ + if (halt) { + if (ep->num == 0) + ep->dev->stall_ep0in = 1; + else { + /* set STALL */ + /* rxfifo empty not taken into acount */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + ep->halted = 1; + +#ifdef UDC_USE_TIMER + /* setup poll timer */ + if (!timer_pending(&udc_pollstall_timer)) { + udc_pollstall_timer.expires = jiffies + HZ + * UDC_POLLSTALL_TIMER_USECONDS + / (1000 * 1000); + if (!stop_pollstall_timer) { + DBG("start polltimer\n"); + add_timer(&udc_pollstall_timer); + } + } +#endif + } + } else { + /* ep is halted by set_halt() before */ + if (ep->halted) { + tmp = readl(&ep->regs->ctl); + /* clear stall bit */ + tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S); + /* clear NAK by writing CNAK */ + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->halted = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } + } + spin_unlock_irqrestore (&udc_stall_spinlock, iflags); + return retval; +} + +/** + * Return fifo fill state (not used, not implemented) + * + * \param usbep pointer to usb ep struct + * \return available bytes in fifo + */ +static int +udc_fifo_status (struct usb_ep *usbep) +{ + return 0; +} + +/** + * Flush the endpoint fifo (not implemented) + * + * \param usbep pointer to usb ep struct + */ +static void +udc_fifo_flush (struct usb_ep *usbep) +{ + return; +} + +/* gadget interface */ +static struct usb_ep_ops udc_ep_ops = { + .enable = udc_enable, + .disable = udc_disable, + + .queue = udc_queue, + .dequeue = udc_dequeue, + + .alloc_request = udc_alloc_request, + .free_request = udc_free_request, + + .alloc_buffer = udc_alloc_buffer, + .free_buffer = udc_free_buffer, + + .set_halt = udc_set_halt, + .fifo_status = udc_fifo_status, + .fifo_flush = udc_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +/** + * Get frame count fifo (not implemented) + * + * \param gadget pointer to gadget struct + * \return frame count + */ +static int udc_get_frame (struct usb_gadget *gadget) +{ + return 0; +} + +/** + * Remote wakeup gadget interface + * + * \param gadget pointer to gadget struct + * \return 0 if success + */ +static int udc_wakeup (struct usb_gadget *gadget) +{ + struct udc *dev; + + if (!gadget) + return -EINVAL; + dev = container_of (gadget, struct udc, gadget); + udc_remote_wakeup(dev); + + return 0; +} + +/** + * gadget ioctl, used for OTG support notification + * + * \param gadget pointer to gadget struct + * \param cmd command + * \param par parmater + * \return 1 if OTG supported, else 0 + */ +static int udc_gadget_ioctl (struct usb_gadget *gadget, unsigned cmd, unsigned long par) +{ + struct udc *dev; + int retval = 0; + unsigned long iflags; + u32 tmp; + + if (!gadget) + return -ENODEV; + dev = container_of (gadget, struct udc, gadget); + spin_lock_irqsave (&dev->lock, iflags); + tmp = readl(&dev->regs->cfg); + + if (tmp & AMD_BIT(UDC_DEVCFG_HNPSFEN)) + retval = 1; + else + retval = 0; + spin_unlock_irqrestore (&dev->lock, iflags); + return retval; +} + +/* gadget operations */ +static const struct usb_gadget_ops udc_ops = { + .wakeup = udc_wakeup, + .get_frame = udc_get_frame, + .ioctl = udc_gadget_ioctl, +}; + +/** + * Setups endpoint parameters, adds endpoints to linked list + * + * \param dev pointer to dev struct + */ +static void make_ep_lists(struct udc *dev) +{ + /* make gadget ep lists */ + INIT_LIST_HEAD (&dev->gadget.ep_list); + list_add_tail (&dev->ep [UDC_EPIN_STATUS_IX].ep.ep_list, &dev->gadget.ep_list); + list_add_tail (&dev->ep [UDC_EPIN_IX].ep.ep_list, &dev->gadget.ep_list); + list_add_tail (&dev->ep [UDC_EPOUT_IX].ep.ep_list, &dev->gadget.ep_list); + + /* fifo config */ + dev->ep [UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE; + if (dev->gadget.speed == USB_SPEED_FULL) + dev->ep [UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE; + else if (dev->gadget.speed == USB_SPEED_HIGH) + dev->ep [UDC_EPIN_IX].fifo_depth = hs_tx_buf; + dev->ep [UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE; +} + +/** + * Sets fifo mode, called by gadget driver + * + * \param gadget pointer to gadget struct + * \param md not used + * \return 0 if success + */ +int au1200udc_set_fifo_mode (struct usb_gadget *gadget, int md) +{ + struct udc *dev; + unsigned long iflags; + + if (!gadget) + return -ENODEV; + + dev = container_of (gadget, struct udc, gadget); + + spin_lock_irqsave (&dev->lock, iflags); + make_ep_lists(dev); + spin_unlock_irqrestore (&dev->lock, iflags); + + return 0; +} +EXPORT_SYMBOL (au1200udc_set_fifo_mode); + +/** + * \brief + * init registers at driver load time + * + * \param dev pointer to device struc + * \return 0 if success + */ +static int startup_registers(struct udc* dev) +{ + u32 tmp; + + /* init controller by soft reset */ + udc_soft_reset(dev); + + /* mask not needed interrupts */ + udc_mask_unused_interrupts(dev); + + /* put into initial config */ + udc_basic_init (dev); + /* link up all endpoints */ + udc_setup_endpoints (dev); + + /* program speed */ + tmp = readl(&dev->regs->cfg); + if (use_fullspeed) { + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); + } + else { + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD); + } + writel(tmp, &dev->regs->cfg); + + return 0; +} + +/** + * Inits UDC context + * + * \param dev pointer to device struct + */ +static void udc_basic_init (struct udc *dev) +{ + u32 tmp; + + DBG("udc_basic_init()\n"); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + +#ifdef UDC_USE_TIMER + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } + /* stop poll stall timer */ + if (timer_pending(&udc_pollstall_timer)) { + mod_timer(&udc_pollstall_timer, jiffies - 1); + } +#endif + /* disable DMA */ + tmp = readl(&dev->regs->ctl); + tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE); + tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE); + writel(tmp, &dev->regs->ctl); + + /* enable dynamic CSR programming */ + tmp = readl(&dev->regs->cfg); + tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG); + /* set self powered */ + tmp |= AMD_BIT(UDC_DEVCFG_SP); + /* set remote wakeupable */ + tmp |= AMD_BIT(UDC_DEVCFG_RWKP); + writel(tmp, &dev->regs->cfg); + + make_ep_lists(dev); + + dev->data_ep_enabled = 0; + dev->data_ep_queued = 0; +} + +/** + * Sets initial endpoint parameters + * + * \param dev pointer to device struct + */ +static void udc_setup_endpoints(struct udc *dev) +{ + struct udc_ep *ep; + u32 tmp; + u32 reg; + + DBG("udc_setup_endpoints()\n"); + + /* read enum speed */ + tmp = readl(&dev->regs->sts); + tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED); + if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) { + dev->gadget.speed = USB_SPEED_HIGH; + } + else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) { + dev->gadget.speed = USB_SPEED_FULL; + } + + /* set basic ep parameters */ + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + ep = &dev->ep[tmp]; + ep->dev = dev; + ep->ep.name = ep_string [tmp]; + ep->num = tmp; + /* txfifo size is calculated at enable time */ + ep->txfifo = dev->txfifo; + + /* fifo size */ + if (tmp < UDC_EPIN_NUM) { + ep->fifo_depth = UDC_TXFIFO_SIZE; + ep->in = 1; + } + else { + ep->fifo_depth = UDC_RXFIFO_SIZE; + ep->in = 0; + + } + ep->regs = &dev->ep_regs[tmp]; + /* ep will be reset only if ep was not enabled before to avoid + disabling ep interrupts when ENUM interrupt occurs but ep is + not enabled by gadget driver */ + if (!ep->desc) { + ep_init (dev->regs, ep); + } + + if (use_dma) { + /* ep->dma is not really used, just to indicate that */ + /* DMA is active: remove this */ + /* dma regs = dev control regs */ + ep->dma = (u32*) &dev->regs->ctl; + + /* nak OUT endpoints until enable - not for ep0*/ + if (tmp != UDC_EP0IN_IX + && tmp != UDC_EP0OUT_IX + && tmp > UDC_EPIN_NUM) { + /* set NAK */ + reg = readl(&dev->ep[tmp].regs->ctl); + reg |= AMD_BIT(UDC_EPCTL_SNAK); + writel(reg, &dev->ep[tmp].regs->ctl); + dev->ep[tmp].naking = 1; + + } + } + } + /* EP0 max packet */ + if (dev->gadget.speed == USB_SPEED_FULL) { + dev->ep [UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE; + dev->ep [UDC_EP0OUT_IX].ep.maxpacket = UDC_FS_EP0OUT_MAX_PKT_SIZE; + } + else if (dev->gadget.speed == USB_SPEED_HIGH) { + dev->ep [UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; + dev->ep [UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + } + + /* with suspend bug workaround, ep0 params for gadget driver + are set at gadget driver bind() call */ + dev->gadget.ep0 = &dev->ep [UDC_EP0IN_IX].ep; + dev->ep [UDC_EP0IN_IX].halted = 0; + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + + /* init cfg/alt/int */ + dev->cur_config = 0; + dev->cur_intf = 0; + dev->cur_alt = 0; +} + +/** + * Bringup after Connect event, + * initial bringup to be ready for ep0 events + * + * \param dev pointer to device struct + */ +static void usb_connect (struct udc *dev) { + + INFO("USB Connect\n"); + + dev->connected = 1; + + /* put into initial config */ + udc_basic_init (dev); + + /* enable device setup interrupts */ + udc_enable_dev_setup_interrupts(dev); +} + +/** + * Calls gadget with disconnect event and resets the UDC and makes + * initial bringup to be ready for ep0 events + * + * \param dev pointer to device struct + */ +static void usb_disconnect (struct udc *dev) { + + INFO("USB Disconnect\n"); + + dev->connected = 0; + + /* mask interrupts */ + udc_mask_unused_interrupts(dev); + + tasklet_schedule(&disconnect_tasklet); +} + +/** + * Tasklet for disconnect to be outside of interrupt + * context + * + * \param par pointer to device struct pointer + */ +void udc_tasklet_disconnect(unsigned long par) +{ + struct udc* dev = (struct udc*) (*((struct udc**) par)); + u32 tmp; + + DBG("Tasklet disconnect\n"); + + if (dev->driver) { + /* call gadget to reset configs etc. */ + if (spin_is_locked(&dev->lock)) { + spin_unlock(&dev->lock); + dev->driver->disconnect (&dev->gadget); + spin_lock(&dev->lock); + } + else + dev->driver->disconnect (&dev->gadget); + + /* empty queues */ + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + empty_req_queue (&dev->ep [tmp]); + } + } + + /* disable ep0 */ + ep_init (dev->regs, + &dev->ep [UDC_EP0IN_IX]); + + + if (!soft_reset_occured) { + /* init controller by soft reset */ +#ifdef UDC_IPCASE_8000018724_WORKAROUND + udc_soft_reset(dev); +#endif + soft_reset_occured++; + } +#ifndef UDC_IPBUG_3950_WORKAROUND + /* re-enable dev interrupts */ + udc_enable_dev_setup_interrupts(dev); +#endif + /* back to full speed ? */ + if (use_fullspeed) { + tmp = readl(&dev->regs->cfg); + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); + writel(tmp, &dev->regs->cfg); + } +} + +/** + * Reset the UDC core + * + * \param dev pointer to device struct + */ +static void udc_soft_reset(struct udc* dev) +{ + DBG("Soft reset\n"); + /* reset possible waiting interrupts, because int. + status is lost after soft reset */ + /* ep int. status reset */ + writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts); + /* device int. status reset */ + writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts); + + spin_lock_irq(&udc_irq_spinlock); + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + readl(&dev->regs->cfg); + spin_unlock_irq(&udc_irq_spinlock); + +} + +#ifdef UDC_USE_TIMER +/** + * RDE timer callback to set RDE bit + * + * \param v timer callback argument + */ +void udc_timer_function(unsigned long v) +{ + u32 tmp; + unsigned int i; + unsigned int bulk_data_arrived; + spin_lock_irq(&udc_irq_spinlock); + if (set_rde) { + /* open the fifo if fifo was filled on last timer call */ + if (set_rde > 1) { + bulk_data_arrived = 0; + /* check wether OUT bulk data is arrived */ + for (i = UDC_EPIN_NUM + 1; i < UDC_EP_NUM; i++) { + tmp = readl(&udc->ep [i].regs->sts); + if (AMD_GETBITS(tmp, UDC_EPSTS_OUT) + == UDC_EPSTS_OUT_DATA) { + bulk_data_arrived = 1; + break; + } + } + /* if OUT bulk data in fifo wait for queueing which + sets RDE, if not open fifo for setup packet */ + if (!bulk_data_arrived) { + /* set RDE */ + tmp = readl(&udc->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RDE); + writel(tmp, &udc->regs->ctl); + } + else + INFO("ep%d - queueing delay longer than %d second(s)\n", + i, UDC_RDE_TIMER_SECONDS); + set_rde = -1; + } + else if (readl(&udc->regs->sts) & + AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + /* if fifo empty setup polling, do not just + open the fifo */ + udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + else { + /* fifo contains data now, setup timer for opening + the fifo when timer expires to be able to receive + setup packets, when data packets gets queued by + gadget layer then timer will forced to expire with + set_rde=0 (RDE is set in udc_queue()) */ + set_rde++; + /* debug: lhadmot_timer_start = 221070 */ + udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + + } + else + set_rde = -1; /* RDE was set by udc_queue() */ + spin_unlock_irq(&udc_irq_spinlock); + if (stop_timer) + complete(&on_exit); + +} + +/** + * Handle halt state, used in stall poll timer + * + * \param ep pointer to endpoint struct + */ +static inline void udc_handle_halt_state(struct udc_ep* ep) +{ + u32 tmp; + /* set stall as long not halted */ + if (ep->halted == 1) { + tmp = readl(&ep->regs->ctl); + /* STALL cleared ? */ + if (!(tmp & AMD_BIT(UDC_EPCTL_S))) { + DBG("ep %d: set STALL again\n", ep->num); + /* set STALL again */ + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } + } +} + +/** + * Stall timer callback to poll S bit and set it again after + * CLEAR_FEATURE + * + * \param v timer callback argument + */ +void udc_pollstall_timer_function(unsigned long v) +{ + struct udc_ep *ep; + int halted = 0; + unsigned long iflags; + + spin_lock_irqsave (&udc_stall_spinlock, iflags); + /* only one IN and OUT endpoints are handled */ + /* IN poll stall */ + ep = &udc->ep [UDC_EPIN_IX]; + udc_handle_halt_state(ep); + if (ep->halted) + halted = 1; + /* OUT poll stall */ + ep = &udc->ep [UDC_EPOUT_IX]; + udc_handle_halt_state(ep); + if (ep->halted) + halted = 1; + + /* setup timer again when still halted */ + if (!stop_pollstall_timer && halted) { + udc_pollstall_timer.expires = jiffies + HZ + * UDC_POLLSTALL_TIMER_USECONDS + / (1000 * 1000); + add_timer(&udc_pollstall_timer); + } + spin_unlock_irqrestore (&udc_stall_spinlock, iflags); + + if (stop_pollstall_timer) + complete(&on_pollstall_exit); +} +#endif + +#if defined(UDC_IPBUG_3950_WORKAROUND) \ + || defined(UDC_IPCASE_8000018724_WORKAROUND) \ + && !defined(UDC_HSB1) +/** + * Called by OTG driver to notify us regarding an OTG event + * + * \param code notify code + */ +void otg_notify(unsigned int code) { + VDBG("OTG notify code=%d\n", code); + switch (code) { + case OTG_GADGET_EVT_SVDROP: + /* disconnect event */ + usb_disconnect(udc); + break; + case OTG_GADGET_EVT_SVALID: + /* connect event */ + usb_connect(udc); + break; + case OTG_GADGET_REQ_WAKE: + /* remote wakeup event */ + udc_remote_wakeup(udc); + break; + } +} +#endif + +/** + * Inits endpoint 0 so that SETUP packets are processed + * + * \param dev pointer to device struct + */ +static void activate_control_endpoints (struct udc *dev) +{ + u32 tmp; + + DBG("activate_control_endpoints\n"); + + /* flush fifo */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_F); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + + /* set ep0 directions */ + dev->ep[UDC_EP0IN_IX].in = 1; + dev->ep[UDC_EP0OUT_IX].in = 0; + + /* set buffer size (tx fifo entries) of EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE, UDC_EPIN_BUFF_SIZE); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE, UDC_EPIN_BUFF_SIZE); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum); + + /* set max packet size of EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt); + + /* set max packet size of EP0_OUT */ + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE); + writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt); + + /* set max packet size of EP0 in UDC CSR */ + tmp = readl(&dev->csr->ne[0]); + if (dev->gadget.speed == USB_SPEED_FULL) + tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE, UDC_CSR_NE_MAX_PKT); + else if (dev->gadget.speed == USB_SPEED_HIGH) + tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE, UDC_CSR_NE_MAX_PKT); + writel(tmp, &dev->csr->ne[0]); + + if (use_dma) { + dev->ep [UDC_EP0OUT_IX].td->status |= AMD_BIT(UDC_DMA_OUT_STS_L); + /* write dma desc address */ + writel(dev->ep [UDC_EP0OUT_IX].td_stp_dma, &dev->ep[UDC_EP0OUT_IX].regs->subptr); + writel(dev->ep [UDC_EP0OUT_IX].td_phys, &dev->ep[UDC_EP0OUT_IX].regs->desptr); +#ifdef UDC_USE_TIMER + /* stop RDE timer */ + if (timer_pending(&udc_timer)) { + set_rde = 0; + mod_timer(&udc_timer, jiffies - 1); + } + /* stop pollstall timer */ + if (timer_pending(&udc_pollstall_timer)) { + mod_timer(&udc_pollstall_timer, jiffies - 1); + } +#endif + /* enable DMA */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_MODE) + | AMD_BIT(UDC_DEVCTL_RDE) + | AMD_BIT(UDC_DEVCTL_TDE); + if (use_dma_bufferfill_mode) { + tmp |= AMD_BIT(UDC_DEVCTL_BF); + } + else if (use_dma_ppb_du) { + tmp |= AMD_BIT(UDC_DEVCTL_DU); + } + writel(tmp, &dev->regs->ctl); + } + + /* clear NAK by writing CNAK for EP0IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX); + + /* clear NAK by writing CNAK for EP0OUT */ + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl); + dev->ep[UDC_EP0OUT_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX); +} + +/** + * \brief + * Make endpoint 0 ready for control traffic + * + * \param dev pointer to device struc + * \return 0 if success + */ +static int setup_ep0(struct udc *dev) +{ + activate_control_endpoints (dev); + /* enable ep0 interrupts */ + udc_enable_ep0_interrupts(dev); + /* enable device setup interrupts */ + udc_enable_dev_setup_interrupts(dev); + + return 0; +} + +/** + * \brief + * Called by gadget driver to register itself + * + * \param driver pointer to gadget driver struct + * \return 0 if success + */ +int usb_gadget_register_driver (struct usb_gadget_driver *driver) +{ + struct udc *dev = udc; + int retval; + u32 tmp; + + if (!driver || !driver->bind + || !driver->unbind + || !driver->setup + || driver->speed != USB_SPEED_HIGH) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + driver->driver.bus = 0; + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + +#ifndef CONFIG_SOC_AU1200 + /*device_create_file (&dev->pdev->dev, &dev_attr_function); + device_create_file (&dev->pdev->dev, &dev_attr_queues);*/ +#endif + +#ifdef CONFIG_USB_OTG + dev->gadget.is_otg = 1; +#endif + retval = driver->bind (&dev->gadget); + /* e.g. ether gadget needs driver_data on both ep0 endpoints */ + dev->ep[UDC_EP0OUT_IX].ep.driver_data = + dev->ep [UDC_EP0IN_IX].ep.driver_data; + + gadget_bind_count++; + if (retval) { + DBG( "binding to %s returning %d\n", + driver->driver.name, retval); + dev->driver = 0; + dev->gadget.dev.driver = 0; + return retval; + } + + /* if otg driver already registered */ + /* call otg bind() to mux udc to phy */ + if (dev->otg_transceiver) { + dev->otg_transceiver->set_peripheral( + dev->otg_transceiver, &dev->gadget); + /* clear SD */ + tmp = readl(&dev->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD); + writel(tmp, &dev->regs->ctl); + } +#ifndef UDC_IPBUG_3950_WORKAROUND + usb_connect(dev); +#endif + + return 0; +} +EXPORT_SYMBOL (usb_gadget_register_driver); + +/** + * Called by OTG driver to register itself + * + * + * \param get_transceiver function pointer to get OTG info + * \return 0 if success + */ +int usb_gadget_register_otg (struct otg_transceiver * (*get_transceiver)(void)) +{ + struct udc *dev = udc; + int retval; + u32 tmp; + + if (!get_transceiver) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->otg_transceiver) + return -EBUSY; + + dev->otg_transceiver = get_transceiver (); + +#if !defined(UDC_HSB1) + if (!(transceiver_to_otg(dev->otg_transceiver)->otg_priv)) + return -EINVAL; + dev->otg_driver = (struct usb_otg_gadget_extension *) + transceiver_to_otg(dev->otg_transceiver)->otg_priv; +#endif + +#ifdef UDC_IPBUG_3943_WORKAROUND + /* init registers here first with suspend bug */ + if (!otg_reg_count) { + startup_registers(dev); + otg_reg_count++; + } +#endif + +#ifdef UDC_IPBUG_3950_WORKAROUND + /* set notify function */ + dev->otg_driver->notify = otg_notify; +#endif + /* if gadget driver already registered */ + /* call gadget bind() to switch to mux udc to phy */ + if (dev->driver) { + + /* otg driver bind() */ + retval = dev->otg_transceiver->set_peripheral( + dev->otg_transceiver, &dev->gadget); + if (retval) { + DBG( "error bind to uoc driver\n"); +#if !defined(UDC_HSB1) + dev->otg_driver = NULL; +#endif + dev->otg_transceiver = NULL; + return retval; + } + /* get ready for ep0 traffic */ + setup_ep0(dev); + + /* clear SD */ + tmp = readl(&dev->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD); + writel(tmp, &dev->regs->ctl); + } + + INFO( "registered uoc driver\n"); + + return 0; +} +EXPORT_SYMBOL (usb_gadget_register_otg); + +/** + * Called by OTG driver to unregister itself + * + * + * \return 0 if success + */ +int usb_gadget_unregister_otg (void) +{ + struct udc *dev = udc; + unsigned long flags; + u32 tmp; + + if (!dev) + return -ENODEV; + + spin_lock_irqsave (&dev->lock, flags); + + /* mask not needed interrupts */ + udc_mask_unused_interrupts(dev); + + spin_unlock_irqrestore (&dev->lock, flags); + + dev->otg_supported = 0; + if (dev->otg_transceiver) { + dev->otg_transceiver->set_peripheral(dev->otg_transceiver, NULL); + dev->otg_transceiver = NULL; + } +#if !defined(UDC_HSB1) + if (dev->otg_driver) { +#ifdef UDC_IPBUG_3950_WORKAROUND + dev->otg_driver->notify = NULL; +#endif + dev->otg_driver = NULL; + } +#endif + + /* set SD */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_SD); + writel(tmp, &dev->regs->ctl); + + DBG( "unregistered uoc driver\n"); + return 0; +} +EXPORT_SYMBOL (usb_gadget_unregister_otg); + +/** + * shutdown requests and disconnect from gadget + */ +static void +shutdown(struct udc *dev, struct usb_gadget_driver *driver) +{ + int tmp; + + /* empty queues and init hardware */ + udc_basic_init(dev); + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + empty_req_queue (&dev->ep [tmp]); + } + + if (dev->gadget.speed != USB_SPEED_UNKNOWN) { + spin_unlock (&dev->lock); + driver->disconnect (&dev->gadget); + spin_lock (&dev->lock); + } + /* init */ + udc_setup_endpoints (dev); +} + +/** + * Called by gadget driver to unregister itself + * + * \param driver pointer to gadget driver struct + * \return 0 if success + */ +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +{ + struct udc *dev = udc; + unsigned long iflags; + u32 tmp; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + if (gadget_bind_count) { + spin_lock_irqsave (&dev->lock, iflags); + shutdown(dev, driver); + spin_unlock_irqrestore (&dev->lock, iflags); + } + + /* unbind from otg driver first */ + if (dev->otg_transceiver) { + dev->otg_transceiver->set_peripheral( + dev->otg_transceiver, NULL); + } + + if (gadget_bind_count) { + driver->unbind (&dev->gadget); + } + gadget_bind_count = 0; + dev->driver = 0; + + /* set SD */ + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_SD); + writel(tmp, &dev->regs->ctl); + + + DBG( "%s: unregistered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL (usb_gadget_unregister_driver); + + +#ifdef UDC_IPBUG_3958_WORKAROUND +/** + * Clear pending NAK bits + * + * \param dev pointer to UDC device object + * \return 0 if success + */ +static void udc_process_cnak_queue(struct udc* dev) +{ + u32 tmp; + u32 reg; + /* check epin's */ + DBG("CNAK pending queue processing\n"); + for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) { + if (cnak_pending & (1 << tmp)) { + DBG("CNAK pending for ep%d\n", tmp); + /* clear NAK by writing CNAK */ + reg = readl(&dev->ep[tmp].regs->ctl); + reg |= AMD_BIT(UDC_EPCTL_CNAK); + writel(reg, &dev->ep[tmp].regs->ctl); + dev->ep[tmp].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num); + } + } + /* ... and ep0out */ + if (cnak_pending & (1 << UDC_EP0OUT_IX)) { + DBG("CNAK pending for ep%d\n", UDC_EP0OUT_IX); + /* clear NAK by writing CNAK */ + reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl); + reg |= AMD_BIT(UDC_EPCTL_CNAK); + writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl); + dev->ep[UDC_EP0OUT_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], + dev->ep[UDC_EP0OUT_IX].num); + } +} +#endif + +/** + * Enabling RX DMA after setup packet + * + * \param dev pointer to UDC device object + */ +static inline void udc_ep0_set_rde(struct udc* dev) +{ + if (use_dma) { +#ifndef UDC_USE_TIMER + udc_set_rde(dev); +#else + /* only enable RXDMA when no data endpoint enabled + or data is queued */ + if (!dev->data_ep_enabled || dev->data_ep_queued) { + udc_set_rde(dev); + } + else { + /* setup timer for enabling RDE (to not enable + RXFIFO DMA for data endpoints to early) */ + if (set_rde != 0 && !timer_pending(&udc_timer)) { + udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; + set_rde = 1; + if (!stop_timer) { + add_timer(&udc_timer); + } + } + } +#endif + } +} + + +/** + * Interrupt handler for data OUT traffic + * + * \param dev pointer to UDC device object + * \param ep_ix endpoint index + * \return 0 if success + */ +static inline int udc_data_out_isr(struct udc* dev, int ep_ix) +{ + int ret_val = 0; + u32 tmp; + struct udc_ep *ep; + struct udc_request *req; + unsigned int count; + struct udc_data_dma *td = NULL; + unsigned dma_done; + + VDBG("ep%d irq\n", ep_ix); + ep = &dev->ep [ep_ix]; + + tmp = readl(&ep->regs->sts); + if (use_dma) { + /* BNA event ? */ + if (tmp & AMD_BIT(UDC_EPSTS_BNA)) { + ERR("BNA ep%dout occured - DESPTR = %lx \n", ep->num, (unsigned long) readl(&ep->regs->desptr)); + + /* clear BNA */ + writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts); + return 1; + } + } + /* HE event ? */ + if (tmp & AMD_BIT(UDC_EPSTS_HE)) { + ERR("HE ep%dout occured\n", ep->num); + + /* clear HE */ + writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); + return 1; + } + + if (!list_empty (&ep->queue)) { + + /* next request */ + req = list_entry (ep->queue.next, + struct udc_request, queue); + } + else + { + req = 0; +#ifdef UDC_DEBUG + no_req++; +#endif + udc_rxfifo_pending = 1; + } + VDBG("req = %lx\n", (unsigned long) req); + /* fifo mode ****************/ + if (!use_dma) { + + /* read fifo */ + if (req && udc_rxfifo_read(ep, req)) { + /* finish */ + complete_req (ep, req, 0); + /* next request */ + if (!list_empty (&ep->queue) && !ep->halted) { + req = list_entry (ep->queue.next, + struct udc_request, queue); + } + else + req = 0; + } + + } /* DMA ********************/ + else if (req) { + + /* check for DMA done */ + if (!use_dma_ppb) { + dma_done = AMD_GETBITS(req->td_data->status, UDC_DMA_OUT_STS_BS); + } /* packet per buffer mode - rx bytes */ + else { + td = udc_get_last_dma_desc(req); + dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS); + } + if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) { + /* buffer fill mode - rx bytes */ + if (!use_dma_ppb) { + /* received number bytes */ + count = AMD_GETBITS(req->td_data->status, UDC_DMA_OUT_STS_RXBYTES); + VDBG("rx bytes=%lx\n", (unsigned long) count); + } /* packet per buffer mode - rx bytes */ + else { + VDBG("req->td_data=%lx\n", (unsigned long) req->td_data); + VDBG("last desc = %lx\n", (unsigned long) td); + /* received number bytes */ + if (use_dma_ppb_du) { + /* every desc. counts bytes */ + count = udc_get_ppbdu_rxbytes(req); + } + else { + /* last desc. counts bytes */ + count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES); + if (!count) { + /* on 64k packets the RXBYTES field is zero */ + if (req->req.length == UDC_DMA_MAXPACKET) + count = UDC_DMA_MAXPACKET; + } + } + VDBG("last desc rx bytes=%lx\n", (unsigned long) count); + } + + tmp = req->req.length - req->req.actual; + if (count > tmp) { + if ((tmp % ep->ep.maxpacket) != 0) { + ERR( "%s: received %d bytes, rx-buffer space = %d bytes => buffer overrun\n", + ep->ep.name, count, tmp); + req->req.status = -EOVERFLOW; + } + count = tmp; + } + req->req.actual += count; + + /* complete request */ + complete_req(ep, req, 0); + + /* next request */ + if (!list_empty (&ep->queue) && !ep->halted) { + req = list_entry(ep->queue.next, + struct udc_request, + queue); + + /* next dma */ + ret_val = prep_dma(ep, req); + if (ret_val != 0) + goto finished; + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + + /* enable DMA */ + udc_set_rde(dev); + } + else { +#ifdef UDC_DEBUG + no_pref_req++; + VDBG("OUT queue empty\n"); +#endif + /* schedule timer for setting RDE if queue remains empty + * to allow ep0 packets pass through */ +#ifdef UDC_USE_TIMER + if (set_rde != 0 && !timer_pending(&udc_timer)) { + udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS; + set_rde = 1; + if (!stop_timer) { + add_timer(&udc_timer); + } + } +#endif + if (ep->num != UDC_EP0OUT_IX) + dev->data_ep_queued = 0; + } + + } + else { + /* RX DMA must be reenabled for each desc in PPBDU mode */ + if (use_dma_ppb_du) { + udc_set_rde(dev); + } + } + + } + +#ifdef UDC_IPBUG_3958_WORKAROUND + /* check pending CNAKS */ + if (cnak_pending) { + /* CNAk processing when rxfifo empty only */ + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + udc_process_cnak_queue(dev); + } + } +#endif + + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_DATA_CLEAR, &ep->regs->sts); +finished: + return ret_val; +} + +/** + * Interrupt handler for data IN traffic + * + * \param dev pointer to UDC device object + * \param ep_ix endpoint index + * \return 0 if success + */ +static inline int udc_data_in_isr(struct udc* dev, int ep_ix) +{ + int ret_val = 0; + u32 tmp; + u32 epsts; + struct udc_ep *ep; + struct udc_request *req; + struct udc_data_dma *td; + unsigned dma_done; + unsigned len; + + ep = &dev->ep[ep_ix]; + + epsts = readl(&ep->regs->sts); + if (use_dma) { + /* BNA ? */ + if (epsts & AMD_BIT(UDC_EPSTS_BNA)) { + ERR("BNA ep%din occured - DESPTR = %08lx \n", + ep->num, (unsigned long) readl(&ep->regs->desptr)); + + /* clear BNA */ + writel(epsts, &ep->regs->sts); + + return 1; + } + } + /* HE event ? */ + if (epsts & AMD_BIT(UDC_EPSTS_HE)) { + ERR("HE ep%dn occured - DESPTR = %08lx \n", + ep->num, (unsigned long) readl(&ep->regs->desptr)); + + /* clear HE */ + writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); + return 1; + } + + /* DMA completion */ + if (epsts & AMD_BIT(UDC_EPSTS_TDC)) { + VDBG("TDC set- completion\n"); + if (!list_empty (&ep->queue)) { + req = list_entry (ep->queue.next, + struct udc_request, queue); + if (req) { + /* lengh bytes transfered */ + /* check dma done of last desc. in PPBDU mode */ + if (use_dma_ppb_du) { + td = udc_get_last_dma_desc(req); + if (td) { + dma_done = + AMD_GETBITS(td->status, UDC_DMA_IN_STS_BS); + /* don't care DMA done */ + /* if (dma_done == UDC_DMA_IN_STS_BS_DMA_DONE) */ + req->req.actual = req->req.length; + } + } + else { + /* assume all bytes transferred */ + /* TODO check error status */ + req->req.actual = req->req.length; + } + + if (req->req.actual == req->req.length) { +#ifdef UDC_IPBUG_2253_WORKAROUND + /* stop NAKing after small packet DMA */ + if (ep->naking) { + /* clear NAK by writing CNAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } +#endif + /* complete req */ + complete_req(ep, req, 0); + req->dma_going = 0; +#ifdef UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE + /* further request available ? */ + if (list_empty (&ep->queue)) { + /* disable interrupt */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + } +#endif + + } + } + } + + } /* status reg has IN bit set and TDC not set (if TDC was handled, + IN must not be handled (UDC defect) ? */ + if ((epsts & AMD_BIT(UDC_EPSTS_IN)) && !(epsts & AMD_BIT(UDC_EPSTS_TDC))) { + if (!list_empty (&ep->queue)) + { + /* next request */ + req = list_entry (ep->queue.next, + struct udc_request, queue); + /* FIFO mode ********/ + if (!use_dma) { + /* write fifo */ + udc_txfifo_write(ep, &(req->req)); + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; + if (req->req.actual == req->req.length + || (len != ep->ep.maxpacket)) { + /* && (!req->req.zero || len != ep->ep.maxpacket)) { */ + /* complete req */ + complete_req (ep, req, 0); + } + } /* DMA *****************/ + else if (req && !req->dma_going) { + VDBG("IN DMA : req=%lx req->td_data=%lx\n", + (unsigned long) req, (unsigned long) req->td_data); + if (req->td_data) { + + req->dma_going = 1; + + /* unset L bit of first desc. for chain */ + if (use_dma_ppb && req->req.length > ep->ep.maxpacket) { + req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); + } + + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + + wb_flush(); + /* set HOST READY */ + req->td_data->status + = AMD_ADDBITS(req->td_data->status, + UDC_DMA_IN_STS_BS_HOST_READY, + UDC_DMA_IN_STS_BS); + + wb_flush(); + +#ifdef UDC_IPBUG_2253_WORKAROUND + /* NAK if small packet until TDC interrupt */ + if (req->req.length < UDC_SMALL_PACKET) { + /* set NAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 1; + wb_flush(); + } +#endif + /* set poll demand bit */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_P); + writel(tmp, &ep->regs->ctl); + } + } + + } + } + /* clear status bits */ + writel(epsts, &ep->regs->sts); + + return ret_val; + +} + +/** + * Interrupt handler for Control OUT traffic + * + * \param dev pointer to UDC device object + * \return 0 if success + */ +static inline int udc_control_out_isr(struct udc* dev) +{ + int ret_val = 0; + u32 tmp; + int setup_supported; + u32 count; + int set = 0; + struct udc_ep *ep; + struct udc_ep *ep_tmp; + + ep = &dev->ep[UDC_EP0OUT_IX]; + + /* clear irq */ + writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts); + + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts); + /* check BNA and clear if set */ + if (tmp & AMD_BIT(UDC_EPSTS_BNA)) { + VDBG("BNA set\n"); + writel(AMD_BIT(UDC_EPSTS_BNA), &dev->ep[UDC_EP0OUT_IX].regs->sts); + } + + /* type of data: SETUP or DATA 0 bytes */ + tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT); + VDBG( "data_typ = %lx\n", (unsigned long) tmp); + /* setup data */ + if (tmp == UDC_EPSTS_OUT_SETUP) { + + ep->dev->stall_ep0in = 0; + dev->waiting_zlp_ack_ep0in = 0; + + /* set NAK for EP0_IN */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 1; + /* get setup data */ + if (use_dma) { + + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_SETUP_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts); + + setup_data.data[0] = dev->ep[UDC_EP0OUT_IX].td_stp->data12; + setup_data.data[1] = dev->ep[UDC_EP0OUT_IX].td_stp->data34; + /* set HOST READY */ + writel(UDC_DMA_STP_STS_BS_HOST_READY, + &dev->ep[UDC_EP0OUT_IX].td_stp->status); + } + else { + /* read fifo */ + udc_rxfifo_read_dwords(dev, setup_data.data, 2); + } + + /* determine direction of control data */ + if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) { + dev->gadget.ep0 = &dev->ep [UDC_EP0IN_IX].ep; + /* enable RDE */ + udc_ep0_set_rde(dev); + set = 0; + } + else { + dev->gadget.ep0 = &dev->ep [UDC_EP0OUT_IX].ep; +#ifdef UDC_USE_TIMER + set = 1; + dev->ep[UDC_EP0OUT_IX].naking = 1; + /* setup timer for enabling RDE (to not enable + RXFIFO DMA for data to early) */ + set_rde = 1; + if (!timer_pending(&udc_timer)) { + udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; + if (!stop_timer) { + add_timer(&udc_timer); + } + } +#endif + } + /* mass storage reset must be processed here because + next packet may be a CLEAR_FEATURE HALT which would not + clear the stall bit when no STALL handshale was received before + (autostall can cause this) */ + if (setup_data.data[0] == UDC_MSCRES_DWORD0 && + setup_data.data[1] == UDC_MSCRES_DWORD1) { + DBG("MSC Reset\n"); + /* clear stall bits */ + /* only one IN and OUT endpoints are handled */ + ep_tmp = &udc->ep [UDC_EPIN_IX]; + udc_set_halt (&ep_tmp->ep, 0); + ep_tmp = &udc->ep [UDC_EPOUT_IX]; + udc_set_halt (&ep_tmp->ep, 0); + } + + /* call gadget with setup data received */ + spin_unlock (&dev->lock); + setup_supported = dev->driver->setup (&dev->gadget, + &setup_data.request); + spin_lock (&dev->lock); + + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + /* ep0 in returns data (not zlp) on IN phase */ + if (setup_supported >= 0 && setup_supported < UDC_EP0IN_MAXPACKET) { + /* clear NAK by writing CNAK in EP0_IN */ + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + dev->ep[UDC_EP0IN_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX); + } + else if (setup_supported < 0) { + /* if unsupported request then stall */ + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + } + else + dev->waiting_zlp_ack_ep0in = 1; + + + /* clear NAK by writing CNAK in EP0_OUT */ + if (!set) { + tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl); + dev->ep[UDC_EP0OUT_IX].naking = 0; + UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX); + } + + if (!use_dma) { + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_SETUP_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts); + } + + } /* data packet 0 bytes */ + else if (tmp == UDC_EPSTS_OUT_DATA) { + /* clear OUT bits in ep status */ + writel(UDC_EPSTS_OUT_DATA_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts); + + /* get setup data: only 0 packet */ + if (use_dma) { + /* no req if 0 packet, just reactivate */ + if (list_empty (&dev->ep[UDC_EP0OUT_IX].queue)) { + VDBG("ZLP\n"); + + /* set HOST READY */ + dev->ep[UDC_EP0OUT_IX].td->status = + AMD_ADDBITS(dev->ep[UDC_EP0OUT_IX].td->status, + UDC_DMA_OUT_STS_BS_HOST_READY, + UDC_DMA_OUT_STS_BS); + /* enable RDE */ + udc_ep0_set_rde(dev); + } + else { + /* control write */ + udc_data_out_isr(dev, UDC_EP0OUT_IX); + /* re-program desc. pointer for possible ZLPs */ + writel(dev->ep [UDC_EP0OUT_IX].td_phys, + &dev->ep[UDC_EP0OUT_IX].regs->desptr); + /* enable RDE */ + udc_ep0_set_rde(dev); + } + } + else { + + /* received number bytes */ + count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts); + count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE); + /* out data for fifo mode not working */ + count = 0; + + /* 0 packet or real data ? */ + if (count != 0) { + udc_data_out_isr(dev, UDC_EP0OUT_IX); + } + else { + /* dummy read confirm */ + readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm); + } + } + } + +#ifdef UDC_IPBUG_3958_WORKAROUND + /* check pending CNAKS */ + if (cnak_pending) { + /* CNAk processing when rxfifo empty only */ + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + udc_process_cnak_queue(dev); + } + } +#endif + + return ret_val; +} + +/** + * Interrupt handler for Control IN traffic + * + * \param dev pointer to UDC device object + * \return 0 if success + */ +static inline int udc_control_in_isr(struct udc* dev) +{ + int ret_val = 0; + u32 tmp; + struct udc_ep *ep; + struct udc_request *req; + unsigned len; + + ep = &dev->ep [UDC_EP0IN_IX]; + + /* clear irq */ + writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts); + + tmp= readl(&dev->ep[UDC_EP0IN_IX].regs->sts); + /* DMA completion */ + if (tmp & AMD_BIT(UDC_EPSTS_TDC)) { + VDBG("isr: TDC clear \n"); +#ifdef UDC_IPBUG_2253_WORKAROUND + /* stop NAKing after small packet DMA */ + if (ep->naking) { + /* clear NAK by writing CNAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_CNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 0; + UDC_QUEUE_CNAK(ep, ep->num); + } +#endif + /* clear TDC bit */ + writel(AMD_BIT(UDC_EPSTS_TDC),&dev->ep[UDC_EP0IN_IX].regs->sts); + } /* status reg has IN bit set ? */ + else if (tmp & AMD_BIT(UDC_EPSTS_IN)) { + if (ep->dma) { + /* clear IN bit */ + writel(AMD_BIT(UDC_EPSTS_IN),&dev->ep[UDC_EP0IN_IX].regs->sts); + } + if (dev->stall_ep0in) { + DBG("stall ep0in\n"); + /* halt ep0in */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } + else { + if (!list_empty (&ep->queue)) + { + /* next request */ + req = list_entry (ep->queue.next, + struct udc_request, queue); + + if (ep->dma) { + /* write desc pointer */ + writel(req->td_phys, &ep->regs->desptr); + /* set HOST READY */ + req->td_data->status + = AMD_ADDBITS(req->td_data->status, + UDC_DMA_STP_STS_BS_HOST_READY, + UDC_DMA_STP_STS_BS); + wb_flush(); + +#ifdef UDC_IPBUG_2253_WORKAROUND + /* NAK if small packet until TDC interrupt */ + if (req->req.length < UDC_SMALL_PACKET) { + /* set NAK */ + tmp = readl(&ep->regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_SNAK); + writel(tmp, &ep->regs->ctl); + ep->naking = 1; + wb_flush(); + } +#endif + /* set poll demand bit */ + tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl); + tmp |= AMD_BIT(UDC_EPCTL_P); + writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl); + + /* all bytes will be transferred */ + req->req.actual = req->req.length; + + /* complete req */ + complete_req(ep, req, 0); + + } + else { + /* write fifo */ + udc_txfifo_write(ep, &(req->req)); + + /* lengh bytes transfered */ + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + + req->req.actual += len; + if (req->req.actual == req->req.length + || (len != ep->ep.maxpacket)) { + /* && (!req->req.zero || len != ep->ep.maxpacket)) { */ + /* complete req */ + complete_req(ep, req, 0); + } + } + + } + } + ep->halted = 0; + dev->stall_ep0in = 0; + if (!ep->dma) { + /* clear IN bit */ + writel(AMD_BIT(UDC_EPSTS_IN),&dev->ep[UDC_EP0IN_IX].regs->sts); + } + } + + return ret_val; +} + + +/** + * Interrupt handler for global device events + * + * \param dev pointer to UDC device object + * \param dev_irq device interrupt bit of DEVINT register + * \return 0 if success + */ +static inline int udc_dev_isr(struct udc* dev, u32 dev_irq) +{ + int ret_val = 0; + u32 tmp; + u32 cfg; + struct udc_ep *ep; + u16 i; + u8 udc_csr_epix; + + /* SET_CONFIG irq ? */ + if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) { + + /* read config value */ + tmp = readl(&dev->regs->sts); + cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG); +#ifdef UDC_DEBUG + /* this is needed for debug only */ + if (cfg == dev->cur_config) { + same_cfg = 1; + } + else { + same_cfg = 0; + } + VDBG("same_cfg=%d\n", same_cfg); +#endif + DBG("SET_CONFIG interrupt: config=%d\n", cfg); + dev->cur_config = cfg; + dev->set_cfg_not_acked = 1; + + /* make usb request for gadget driver */ + memset(&setup_data, 0 , sizeof(union udc_setup_data)); + setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.request.wValue = dev->cur_config; + + /* programm the NE registers */ + /* TODO - put this to extra function or use udc_setup_endpoints() or udc_enable() */ + for (i = 0; i < UDC_EP_NUM; i++) { + ep = &dev->ep[i]; + if (ep->in) { + + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num; + + + } /* OUT ep */ + else { + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; + } + + tmp = readl(&dev->csr->ne[udc_csr_epix]); + /* ep cfg */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG); + /* write reg */ + writel(tmp, &dev->csr->ne[udc_csr_epix]); + + /* clear stall bits */ + ep->halted = 0; + tmp = readl(&ep->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } + /* call gadget zero with setup data received */ + spin_unlock (&dev->lock); + tmp = dev->driver->setup (&dev->gadget, &setup_data.request); + spin_lock (&dev->lock); + + } /* SET_INTERFACE ? */ + if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) { + dev->set_cfg_not_acked = 1; + /* read interface and alt setting values */ + tmp = readl(&dev->regs->sts); + dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT); + dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF); + + /* make usb request for gadget driver */ + memset(&setup_data, 0 , sizeof(union udc_setup_data)); + setup_data.request.bRequest = USB_REQ_SET_INTERFACE; + setup_data.request.bRequestType = USB_RECIP_INTERFACE; + setup_data.request.wValue = dev->cur_alt; + setup_data.request.wIndex = dev->cur_intf; + + DBG("SET_INTERFACE interrupt: alt=%d intf=%d\n", dev->cur_alt, dev->cur_intf); + + /* programm the NE registers */ + /* TODO - put this to extra function or use udc_setup_endpoints() or udc_enable() */ + for (i = 0; i < UDC_EP_NUM; i++) { + ep = &dev->ep[i]; + if (ep->in) { + + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num; + + + } /* OUT ep */ + else { + /* ep ix in UDC CSR register space */ + udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; + } + + /***** UDC CSR reg ****************************/ + /* set ep values */ + tmp = readl(&dev->csr->ne[udc_csr_epix]); + /* ep interface */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF); + /* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */ + /* ep alt */ + tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT); + /* tmp = AMD_ADDBITS(tmp, 1, UDC_CSR_NE_ALT); */ + /* write reg */ + writel(tmp, &dev->csr->ne[udc_csr_epix]); + + /* clear stall bits */ + ep->halted = 0; + tmp = readl(&ep->regs->ctl); + tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S); + writel(tmp, &ep->regs->ctl); + } + + /* call gadget zero with setup data received */ + spin_unlock (&dev->lock); + tmp = dev->driver->setup (&dev->gadget, &setup_data.request); + spin_lock (&dev->lock); + + } /* USB reset */ + if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) { + DBG("USB Reset interrupt\n"); + + /* allow soft reset when suspend occurs */ + soft_reset_occured = 0; + + dev->waiting_zlp_ack_ep0in = 0; + dev->set_cfg_not_acked = 0; + + /* mask not needed interrupts */ + udc_mask_unused_interrupts(dev); + + /* call gadget to reset configs etc. */ + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + + /* disable ep0 to empty req queue */ + empty_req_queue (&dev->ep [UDC_EP0IN_IX]); + ep_init(dev->regs, + &dev->ep [UDC_EP0IN_IX]); + +#ifdef UDC_IPBUG_3958_WORKAROUND_SOFT_RESET_ON_USBRESET + /* soft reset when rxfifo not empty */ + tmp = readl(&dev->regs->sts); + if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) && + !soft_reset_after_usbreset_occured) { + udc_soft_reset(dev); + soft_reset_after_usbreset_occured++; + } +#endif + + /* DMA reset to kill potential old DMA hw hang, */ + /* POLL bit is already reset by ep_init() through */ + /* disconnect() */ + UDC_DMARST(tmp,dev); + + /* put into initial config */ + udc_basic_init (dev); + + /* enable device setup interrupts */ + udc_enable_dev_setup_interrupts(dev); + + } /* USB suspend */ +#ifndef UDC_IPBUG_3943_WORKAROUND + if (dev_irq & AMD_BIT(UDC_DEVINT_US)) { + DBG("USB Suspend interrupt\n"); + + } /* new speed ? */ +#endif + if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) { + DBG("ENUM interrupt\n"); +#ifdef UDC_DEBUG + num_enums++; + DBG("%d enumerations !\n", num_enums); +#endif + soft_reset_after_usbreset_occured = 0; + + /* disable ep0 to empty req queue */ + empty_req_queue (&dev->ep [UDC_EP0IN_IX]); + ep_init (dev->regs, + &dev->ep [UDC_EP0IN_IX]); + + /* link up all endpoints */ + udc_setup_endpoints (dev); + if (dev->gadget.speed == USB_SPEED_HIGH) { + INFO("Connect: Speed = HIGH_SPEED\n"); + } + else if (dev->gadget.speed == USB_SPEED_FULL) { + INFO("Connect: Speed = FULL_SPEED\n"); + } + + /* init ep 0 */ + activate_control_endpoints(dev); + + /* enable ep0 interrupts */ + udc_enable_ep0_interrupts(dev); + } +#ifndef UDC_IPBUG_3950_WORKAROUND + /* session valid change interrupt */ + if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) { + DBG("USB SVC interrupt\n"); + + /* check that session is not valid to detect disconnect */ + tmp = readl(&dev->regs->sts); + if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) { + DBG("USB Disconnect (session valid low)\n"); + /* cleanup on disconnect */ + usb_disconnect(udc); + } + + } +#endif + + return ret_val; +} + +/** + * Interrupt Service Routine, see Linux Kernel Doc for parameters + * + * \param irq irq number + * \param pdev pointer to device object + * \param ptregs don't used + */ +static irqreturn_t udc_irq (int irq, void *pdev, struct pt_regs * ptregs) +{ + struct udc *dev = pdev; + u32 reg; + u16 i; + u32 ep_irq; + + +#ifdef UDC_IPBUG_3943_WORKAROUND + /* If UDC is suspended, then don't touch any register, otherwise + system hangs in endless retry => possibly hang !!! */ + if (dev->otg_driver && dev->otg_driver->query) { + if (dev->otg_driver->query(0) & OTG_FLAGS_UDC_SUSP) { + return IRQ_HANDLED; + } + } + else + return IRQ_HANDLED; +#endif + + if (dev->sys_suspended) + return IRQ_HANDLED; + + spin_lock (&dev->lock); + + + /* check for ep irq */ + reg = readl(&dev->regs->ep_irqsts); + if (reg) + { + /* EP0 OUT */ + if (reg & AMD_BIT(UDC_EPINT_OUT_EP0)) + { + udc_control_out_isr(dev); + } /* EP0 IN */ + if (reg & AMD_BIT(UDC_EPINT_IN_EP0)) { + udc_control_in_isr(dev); + + } + + /* data endpoint */ + /* iterate ep's */ + for (i = 1; i < UDC_EP_NUM; i++) { + ep_irq = 1 << i; + /* irq for out ep ? */ + if ((reg & ep_irq) && i > UDC_EPIN_NUM) { + /* clear irq */ + writel(ep_irq, &dev->regs->ep_irqsts); + udc_data_out_isr(dev,i); + } /* irq for in ep ? */ + if ((reg & ep_irq) && i < UDC_EPIN_NUM && i > 0) { + /* clear irq */ + writel(ep_irq, &dev->regs->ep_irqsts); + udc_data_in_isr(dev,i); + } + + } + + } + + + /* check for dev irq */ + reg = readl(&dev->regs->irqsts); + if (reg) { + /* clear irq */ + writel(reg, &dev->regs->irqsts); + udc_dev_isr(dev, reg); + } + + + spin_unlock (&dev->lock); + return IRQ_HANDLED; +} + +/** + * Tears down device + * + * \param pdev pointer to device struct + */ +static void gadget_release (struct device *pdev) +{ + struct au1200udc *dev = dev_get_drvdata(pdev); + kfree (dev); +} + +/** + * Cleanup on device remove + * + * \param dev pointer to udc struct + */ +static void udc_remove(struct udc* dev) +{ +#ifdef CONFIG_SOC_AU1200 + { + u32 tmp; + /* disable UDC memory, DMA and clock */ + tmp = readl((u32*) (USB_MSR_BASE + USB_MSR_MCFG)); + tmp &= AMD_CLEAR_BIT(USBMSRMCFG_DMEMEN) + & AMD_CLEAR_BIT(USBMSRMCFG_DBMEN) + & AMD_CLEAR_BIT(USBMSRMCFG_UDCCLKEN); + writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); + } +#endif + +#if defined(CONFIG_SOC_AU1200) + /* done by gadget.dev.release() in kernel 2.6.x + * TMP26: device registration currently disabled for Au1200 */ + kfree (dev); +#endif +#ifdef UDC_USE_TIMER + /* remove timer */ + stop_timer++; + if (timer_pending(&udc_timer)) + wait_for_completion(&on_exit); + if (udc_timer.data) + del_timer_sync(&udc_timer); + /* remove pollstall timer */ + stop_pollstall_timer++; + if (timer_pending(&udc_pollstall_timer)) + wait_for_completion(&on_pollstall_exit); + if (udc_pollstall_timer.data) + del_timer_sync(&udc_pollstall_timer); +#endif + udc = 0; +} + +#if defined(CONFIG_SOC_AU1200) +/** + * Reset all context + * + * \param pdev pointer to device struct + */ +static void udc_drv_remove (struct device *_dev) +{ + struct platform_device *pdev = to_platform_device(_dev); + struct udc *dev = dev_get_drvdata (_dev); + +#ifdef UDC_DEBUG + /* debug */ + print_misc(dev); +#endif + /* gadget driver registered ? */ + if (dev->driver) { + WARN( "unregistering %s on driver remove\n", dev->driver->driver.name); + usb_gadget_unregister_driver (dev->driver); + } + /* otg driver registered ? */ + if (dev->otg_transceiver) { + /* should have been done already by driver model core */ + WARN( "uoc driver is still registered\n"); + } + /* dma pool cleanup */ + if (dev->data_requests) { + dma_pool_destroy (dev->data_requests); + } + if (dev->stp_requests) { + /* cleanup DMA desc's for ep0in */ + dma_pool_free (dev->stp_requests, + dev->ep [UDC_EP0OUT_IX].td_stp, + dev->ep [UDC_EP0OUT_IX].td_stp_dma); + dma_pool_free (dev->stp_requests, + dev->ep [UDC_EP0OUT_IX].td, + dev->ep [UDC_EP0OUT_IX].td_phys); + + dma_pool_destroy (dev->stp_requests); + } + + /* init controller by soft reset */ + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + + if (dev->irq_registered) + free_irq (pdev->resource[1].start, dev); + if (dev->regs) + iounmap (dev->regs); + if (dev->mem_region) + release_mem_region (pdev->resource[0].start, + pdev->resource[0].end + 1 + - pdev->resource[0].start); + + device_unregister (&dev->gadget.dev); + /* device_remove_file (&pdev->dev, &dev_attr_registers); */ + dev_set_drvdata (_dev, 0); + + udc_remove(dev); +} +#endif + +#if !defined(CONFIG_SOC_AU1200) +/** + * Reset all pci context + * + * \param pdev pointer to pci device struct + */ +static void udc_pci_remove (struct pci_dev *pdev) +{ + struct udc *dev; + + dev = pci_get_drvdata(pdev); +#ifdef UDC_DEBUG + /* debug */ + print_misc(dev); +#endif + /* gadget driver registered ? */ + if (dev->driver) { + WARN( "unregistering %s on pci remove\n", dev->driver->driver.name); + usb_gadget_unregister_driver (dev->driver); + } + /* otg driver registered ? */ + if (dev->otg_transceiver) { + /* should have been done already by driver model core */ + WARN( "uoc driver is still registered\n"); + } + /* dma pool cleanup */ + if (dev->data_requests) { + dma_pool_destroy (dev->data_requests); + } + if (dev->stp_requests) { + /* cleanup DMA desc's for ep0in */ + dma_pool_free (dev->stp_requests, + dev->ep [UDC_EP0OUT_IX].td_stp, + dev->ep [UDC_EP0OUT_IX].td_stp_dma); + dma_pool_free (dev->stp_requests, + dev->ep [UDC_EP0OUT_IX].td, + dev->ep [UDC_EP0OUT_IX].td_phys); + + dma_pool_destroy (dev->stp_requests); + } + + /* init controller by soft reset */ + writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); + if (dev->irq_registered) + free_irq (pdev->irq, dev); + if (dev->regs) + iounmap (dev->regs); + if (dev->mem_region) + release_mem_region (pci_resource_start (pdev, 0), + pci_resource_len (pdev, 0)); +#ifndef USE_AU1200_PCI_DUMMY + if (dev->active) + pci_disable_device (pdev); +#endif + + /* device file system not working for Au1200 */ + device_unregister (&dev->gadget.dev); + /* device_remove_file (&pdev->dev, &dev_attr_registers); */ + pci_set_drvdata (pdev,0); + + udc_remove(dev); +} +#endif + +/** + * create dma pools on init + * + * \param dev pointer to udc device struct + * \return 0 if success + */ +__init static int init_dma_pools(struct udc* dev) +{ + struct udc_stp_dma *td_stp; + struct udc_data_dma *td_data; + int retval; + + /* consistent DMA mode setting ? */ + if (use_dma_ppb) { + use_dma_bufferfill_mode = 0; + } + else { + use_dma_ppb_du = 0; + use_dma_bufferfill_mode = 1; + } + + /* DMA setup */ + dev->data_requests = dma_pool_create ("data_requests", NULL, + sizeof (struct udc_data_dma), + UDC_PCIPOOL_ALIGN, + UDC_PCIPOOL_CROSS); + if (!dev->data_requests) { + DBG( "can't get request data pool\n"); + retval = -ENOMEM; + goto finished; + } + + /* EP0 in dma regs = dev control regs */ + dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl; + + /* dma desc for setup data */ + dev->stp_requests = dma_pool_create ("setup requests", NULL, + sizeof (struct udc_stp_dma), + UDC_PCIPOOL_ALIGN, + UDC_PCIPOOL_CROSS); + if (!dev->stp_requests) { + DBG( "can't get stp request pool\n"); + retval = -ENOMEM; + goto finished; + } + /* setup */ + td_stp = dma_pool_alloc (dev->stp_requests, UDC_PCIPOOL_GFP_STP, + &dev->ep [UDC_EP0OUT_IX].td_stp_dma); + if (td_stp == NULL){ + retval = -ENOMEM; + goto finished; + } + dev->ep [UDC_EP0OUT_IX].td_stp = td_stp; + /* data: 0 packets !? */ + td_data = dma_pool_alloc (dev->stp_requests, UDC_PCIPOOL_GFP_STP, + &dev->ep [UDC_EP0OUT_IX].td_phys); + if (td_data == NULL){ + retval = -ENOMEM; + goto finished; + } + dev->ep [UDC_EP0OUT_IX].td = td_data; +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + /* point to itself */ + dev->ep [UDC_EP0OUT_IX].td->next = dev->ep [UDC_EP0OUT_IX].td_phys; +#endif + return 0; + +finished: + return retval; +} + +#if defined(CONFIG_SOC_AU1200) +/** + * Called by kernel init device context + * + * \param dev pointer to device struct + * \return 0 if success + */ +static int udc_drv_probe (struct device* _dev) +{ + char tmp[8]; + struct udc *dev; + struct platform_device *pdev = to_platform_device(_dev); + u32 resource; + u32 len; + u32 irq; + int retval = 0; +#ifdef CONFIG_SOC_AU1200 + u32 reg; + + /* basic init */ + reg = readl((u32*) (USB_MSR_BASE + USB_MSR_MCFG)); + if (reg == 0) { + /* default value */ + reg = USBMSRMCFG_DEFAULT; + writel(reg, USB_MSR_BASE + USB_MSR_MCFG); + readl((u32*) USB_MSR_BASE + USB_MSR_MCFG); + udelay(1000); + } + /* enable UDC memory, DMA, clock, cacheable memory, + * read combining and prefetch enable */ + reg |= AMD_BIT(USBMSRMCFG_DMEMEN) | AMD_BIT(USBMSRMCFG_DBMEN) + | AMD_BIT(USBMSRMCFG_UDCCLKEN) + | AMD_BIT(USBMSRMCFG_PHYPLLEN) +#ifdef CONFIG_DMA_COHERENT + | AMD_BIT(USBMSRMCFG_UCAM) +#endif + | AMD_BIT(USBMSRMCFG_RDCOMB) + | AMD_BIT(USBMSRMCFG_PFEN); + writel(reg, USB_MSR_BASE + USB_MSR_MCFG); +#endif + + /* one udc only */ + if (udc) { + WARN("already probed: %04x/%04x\n", UDC_PCI_VENID, UDC_PCI_DEVID); + return -EBUSY; + } + + /* init */ + dev = kmalloc (sizeof (struct udc), SLAB_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto finished; + } + memset (dev, 0, sizeof(struct udc)); + + dev->pdev = _dev; + + /* check platform resources */ + if (pdev->resource[0].flags != IORESOURCE_MEM) { + ERR ("resource[0] must be IORESOURCE_MEM\n"); + retval = -ENOMEM; + goto finished; + } + resource = pdev->resource[0].start; + len = pdev->resource[0].end + 1 - pdev->resource[0].start; + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + ERR ("resource[1] must be IORESOURCE_IRQ\n"); + retval = -ENOMEM; + goto finished; + } + irq = pdev->resource[1].start; + + /* platform device resource allocation */ + /* mem */ + if (!request_mem_region (resource, len, name)) { + ERR ("controller already in use\n"); + retval = -EBUSY; + goto finished; + } + dev->mem_region = 1; + + dev->virt_addr = ioremap_nocache(resource, len); + if (dev->virt_addr == NULL) { + DBG( "start address cannot be mapped\n"); + retval = -EFAULT; + goto finished; + } + + /* irq */ + if (!irq) { + ERR( "irq not set\n"); + retval = -ENODEV; + goto finished; + } + snprintf (tmp, sizeof tmp, "%d", irq); + if (request_irq (irq, udc_irq, SA_SHIRQ, name, dev) != 0) { + ERR( "error on request_irq() with %s\n", tmp); + retval = -EBUSY; + goto finished; + } + dev->irq_registered = 1; + + dev_set_drvdata (_dev, dev); + + /* chip revision */ + dev->chiprev = 0; + + /* chip rev for Au1200 */ + dev->chiprev = (u16) read_c0_prid() & 0xff; + + /* init dma pools */ + if (use_dma) { + retval = init_dma_pools(dev); + if (retval != 0) + goto finished; + } + + dev->phys_addr = resource; + dev->irq = irq; + dev->gadget.dev.parent = _dev; + dev->gadget.dev.dma_mask = _dev->dma_mask; + /* general probing */ + if (udc_probe(dev) != 0) + goto finished; + return retval; + +finished: + if (dev) + udc_drv_remove (_dev); + return retval; +} +#endif + +#if !defined(CONFIG_SOC_AU1200) +/** + * Called by pci bus driver to init pci context + * + * \param pdev pointer to pci device struct + * \param id pointer to pci device id + * \return 0 if success + */ +static int udc_pci_probe (struct pci_dev *pdev, const struct pci_device_id *id) +{ + char tmp[8]; + struct udc *dev; + unsigned long resource; + unsigned long len; + int retval = 0;; + + VDBG("udc_pci_probe()\n"); + +#ifdef CONFIG_SOC_AU1200 + { + u32 tmp; + /* + * Fill in the dummy pci_dev + */ + memset(pdev, 0, sizeof(struct pci_dev)); + pdev->resource[0].name = "UDC Registers"; + pdev->resource[0].start = USB_UDC_BASE; + pdev->resource[0].end = USB_UDC_BASE + USB_UDC_LEN - 1; + pdev->resource[0].flags = 0; + pdev->irq = AU1200_USB_INT; + + tmp = readl((u32*) (USB_MSR_BASE + USB_MSR_MCFG)); + if (tmp == 0) { + /* default value */ + tmp = USBMSRMCFG_DEFAULT; + writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); + readl((u32*) USB_MSR_BASE + USB_MSR_MCFG); + udelay(1000); + } + /* enable UDC memory, DMA, clock, cacheable memory, + * read combining and prefetch enable */ + tmp |= AMD_BIT(USBMSRMCFG_DMEMEN) | AMD_BIT(USBMSRMCFG_DBMEN) + | AMD_BIT(USBMSRMCFG_UDCCLKEN) + | AMD_BIT(USBMSRMCFG_PHYPLLEN) +#ifdef CONFIG_DMA_COHERENT + | AMD_BIT(USBMSRMCFG_UCAM) +#endif + | AMD_BIT(USBMSRMCFG_RDCOMB) + | AMD_BIT(USBMSRMCFG_PFEN); + writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); + } +#endif + /* one udc only */ + if (udc) { + WARN("already probed: %04x/%04x\n", UDC_PCI_VENID, UDC_PCI_DEVID); + return -EBUSY; + } + + /* init */ + dev = kmalloc (sizeof (struct udc), SLAB_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto finished; + } + memset (dev, 0, sizeof(struct udc)); + + /* pci setup */ +#ifndef USE_AU1200_PCI_DUMMY + if (pci_enable_device (pdev) < 0) { + retval = -ENODEV; + goto finished; + } +#endif + dev->active = 1; + + /* PCI resource allocation */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len (pdev, 0); + + if (!request_mem_region (resource, len, name)) { + DBG( "pci device used already\n"); + retval = -EBUSY; + goto finished; + } + dev->mem_region = 1; + + dev->virt_addr = ioremap_nocache(resource, len); + if (dev->virt_addr == NULL) { + DBG( "start address cannot be mapped\n"); + retval = -EFAULT; + goto finished; + } + + if (!pdev->irq) { + ERR( "pdev->irq not set\n"); + retval = -ENODEV; + goto finished; + } + + snprintf (tmp, sizeof tmp, "%d", pdev->irq); + + if (request_irq (pdev->irq, udc_irq, SA_SHIRQ, name, dev) != 0) { + ERR( "error on request_irq() with %s\n", tmp); + retval = -EBUSY; + goto finished; + } + dev->irq_registered = 1; + + pci_set_drvdata (pdev, dev); + + /* chip revision */ + dev->chiprev = 0; + +#ifndef CONFIG_SOC_AU1200 + pci_set_master (pdev); +#ifdef HAVE_PCI_SET_MWI + pci_set_mwi (pdev); +#endif + /* chip rev for Hs AU1200 */ + pci_read_config_byte(pdev, PCI_REVISION_ID, (u8*) &dev->chiprev); +#else + /* chip rev for Au1200 */ + dev->chiprev = (u16) read_c0_prid() & 0xff; +#endif + /* init dma pools */ + if (use_dma) { + retval = init_dma_pools(dev); + if (retval != 0) + goto finished; + } + + dev->phys_addr = resource; + dev->irq = pdev->irq; + dev->pdev = pdev; + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + + /* general probing */ + if (udc_probe(dev) != 0) + goto finished; + return retval; + +finished: + if (dev) + udc_pci_remove (pdev); + return retval; +} +#endif + +/** + * general probe + * + * \param dev pointer to udc device struct + * \return 0 if success + */ +__init int udc_probe(struct udc* dev) +{ + char tmp[128]; + u32 reg; + int retval; + + /* mark timer as not initialized */ + udc_timer.data = 0; + udc_pollstall_timer.data = 0; + + /* device struct setup */ + spin_lock_init(&dev->lock); + spin_lock_init(&udc_irq_spinlock); + spin_lock_init(&udc_stall_spinlock); + dev->gadget.ops = &udc_ops; + + strcpy(dev->gadget.dev.bus_id, "gadget"); + dev->gadget.dev.release = gadget_release; + dev->gadget.name = name; + dev->gadget.is_dualspeed = 1; + + /* udc csr registers base */ + dev->csr = (struct udc_csrs*) (dev->virt_addr + UDC_CSR_ADDR); + /* dev registers base */ + dev->regs = (struct udc_regs *) (dev->virt_addr + UDC_DEVCFG_ADDR); + /* ep registers base */ + dev->ep_regs = (struct udc_ep_regs *) (dev->virt_addr + UDC_EPREGS_ADDR); + /* fifo's base */ + dev->rxfifo = (u32*) (dev->virt_addr + UDC_RXFIFO_ADDR); + dev->txfifo = (u32*) (dev->virt_addr + UDC_TXFIFO_ADDR); + + /* init registers, interrupts, ... */ +#ifndef UDC_IPBUG_3943_WORKAROUND + startup_registers(dev); +#else + { + u32 tmp; + + /* TODO put this to extra function, + * this all is extracted from usb_init() and + * udc_basic_init() but without register access */ + dev->gadget.ep0 = &dev->ep [UDC_EP0IN_IX].ep; + dev->ep [UDC_EP0IN_IX].halted = 0; + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + dev->gadget.speed = USB_SPEED_HIGH; + make_ep_lists(dev); + /* basic endpoint init */ + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + struct udc_ep *ep = &dev->ep[tmp]; + + ep->ep.name = ep_string[tmp]; + ep->dev = dev; + ep->num = tmp; + /* txfifo size is calculated at enable time */ + ep->txfifo = dev->txfifo; + + /* fifo size */ + if (tmp < UDC_EPIN_NUM) { + ep->fifo_depth = UDC_TXFIFO_SIZE; + ep->in = 1; + } + else { + ep->fifo_depth = UDC_RXFIFO_SIZE; + ep->in = 0; + + } + + ep->regs = &dev->ep_regs [tmp]; + if (!ep->desc) { + ep->desc = 0; + INIT_LIST_HEAD (&ep->queue); + + ep->ep.maxpacket = ~0; + ep->ep.ops = &udc_ep_ops; + } + if (use_dma) { + /* ep->dma is not really used, just to indicate that */ + /* DMA is active */ + /* dma regs = dev control regs */ + ep->dma = (u32*) &dev->regs->ctl; + } + } + dev->ep [UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; + dev->ep [UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + } +#endif + + + INFO( "%s\n", mod_desc); + + snprintf (tmp, sizeof tmp, "%d", dev->irq); +#ifdef CONFIG_SOC_AU1200 + INFO( "irq %s, mem %08lx, chip rev %02x (Au1200 %s)\n", + tmp, dev->phys_addr, dev->chiprev, (dev->chiprev == 0) ? "AB" : "AC"); + strcpy(tmp, UDC_DRIVER_VERSION_STRING); +#ifdef CONFIG_DMA_COHERENT + /* coherent DMA not possible with AB silicon */ + if (dev->chiprev == UDC_AUAB_REV) { + ERR("Your chip revision is %s, it must be at least %s to use coherent DMA. \nPlease change DMA_COHERENT to DMA_NONCOHERENT in arch/mips/Kconfig and re-compile .\n", + "AB", "AC"); + retval = -ENODEV; + goto finished; + } +#endif + +#ifdef UDC_AUA1 + if (dev->chiprev < UDC_AUA1) { + ERR("Your chip revision is %s, it must be at least %s\n", + "AB", "AC"); + retval = -ENODEV; + goto finished; + } + INFO("driver version: %s (for Au1200 AC)\n", tmp); +#else + INFO("driver version: %s (for Au1200 AB)\n", tmp); +#endif +#ifdef CONFIG_DMA_COHERENT + INFO("Compiled for coherent memory.\n"); +#endif +#ifdef CONFIG_DMA_NONCOHERENT + INFO("Compiled for non-coherent memory.\n"); +#endif +#else + INFO("irq %s, pci mem %08lx, chip rev %02x (Geode5536 %s)\n", + tmp, dev->phys_addr, dev->chiprev, (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); + strcpy(tmp, UDC_DRIVER_VERSION_STRING); +#ifdef UDC_HSB1 + if (dev->chiprev == UDC_HSA0_REV) { + ERR("Your chip revision is %s, it must be at least %s\n", + "A0", "B1"); + retval = -ENODEV; + goto finished; + } + INFO("driver version: %s (for Geode5536 B1)\n", tmp); +#else + INFO("driver version: %s (for Geode5536 A0)\n", tmp); +#endif +#endif + udc = dev; + + device_register (&dev->gadget.dev); + /* device_create_file (&pdev->dev, &dev_attr_registers); */ + +#ifdef UDC_USE_TIMER + /* timer init */ + init_timer(&udc_timer); + udc_timer.function = udc_timer_function; + udc_timer.data = 1; + /* timer pollstall init */ + init_timer(&udc_pollstall_timer); + udc_pollstall_timer.function = udc_pollstall_timer_function; + udc_pollstall_timer.data = 1; +#endif + + /* set SD */ + reg = readl(&dev->regs->ctl); + reg |= AMD_BIT(UDC_DEVCTL_SD); + writel(reg, &dev->regs->ctl); + + /* print dev register info */ + print_regs(dev); + + return 0; + +finished: + return retval; +} + + +/** + * Initiates a remote wakeup + * + * \return 0 if success + */ +/* initiate remote wakeup */ +static int udc_remote_wakeup(struct udc* dev) +{ + u32 tmp; + + INFO("UDC initiates remote wakeup\n"); + + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RES); + writel(tmp, &dev->regs->ctl); + /* wait 1ms before clear resume bit */ + /* udelay(1000); */ + tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES); + writel(tmp, &dev->regs->ctl); + + return 0; +} + +#if defined(CONFIG_SOC_AU1200) +/** + * Suspends UDC + * + * \return 0 if success + */ +static int udc_suspend(struct udc* dev) +{ + int retval = 0; + + INFO("UDC suspend\n"); +#ifdef CONFIG_SOC_AU1200 + u32 tmp; + /* mask interrupts */ + udc_mask_unused_interrupts(dev); + + if (dev->driver && dev->driver->disconnect) { + /* call gadget to reset context */ + if (spin_is_locked(&dev->lock)) { + spin_unlock(&dev->lock); + dev->driver->disconnect (&dev->gadget); + spin_lock(&dev->lock); + } + else + dev->driver->disconnect (&dev->gadget); + + /* disable ep0 to empty req queue */ + empty_req_queue (&dev->ep [UDC_EP0IN_IX]); + ep_init (dev->regs, + &dev->ep [UDC_EP0IN_IX]); + + /* init controller by soft reset */ + udc_soft_reset(dev); + + } + if (dev->otg_driver && dev->otg_transceiver + && dev->otg_transceiver->set_peripheral) { + /* if UDC is supended by Host or already disconnected then + don't force disconnect by unbind() */ + if (dev->otg_driver->query) { + if (!(dev->otg_driver->query(0) & OTG_FLAGS_UDC_SUSP)) { + /* unbind from otg driver -> host disconnect */ + dev->otg_transceiver->set_peripheral(dev->otg_transceiver, NULL); + dev->connected = 0; + } + } + else + { + /* unbind from otg driver -> host disconnect */ + dev->otg_transceiver->set_peripheral (dev->otg_transceiver, NULL); + dev->connected = 0; + } + } + + dev->sys_suspended = 1; + + /* switch off UDC clock */ + tmp = readl((u32*) (USB_MSR_BASE + USB_MSR_MCFG)); + tmp &= AMD_CLEAR_BIT(USBMSRMCFG_UDCCLKEN); + writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); + +#endif + + return retval; +} + +/** + * Resumes UDC + * + * \return 0 if success + */ +static int udc_resume(struct udc* dev) +{ + int retval = 0; + + INFO("UDC resume\n"); +#ifdef CONFIG_SOC_AU1200 + u32 tmp; + /* switch on UDC clock */ + tmp = readl((u32*) (USB_MSR_BASE + USB_MSR_MCFG)); + tmp |= AMD_BIT(USBMSRMCFG_UDCCLKEN); + writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); + + dev->sys_suspended = 0; + + usb_connect(dev); + if (dev->otg_transceiver && dev->otg_transceiver->set_peripheral) { + /* bind to otg driver */ + dev->otg_transceiver->set_peripheral(dev->otg_transceiver, &dev->gadget); + } + +#endif + + return retval; +} + +static int udc_au1xxx_drv_probe(struct device *dev) +{ + int retval; + + DBG("udc_au1xxx_drv_probe()\n"); + retval = udc_drv_probe(dev); + return retval; +} + +static int udc_au1xxx_drv_remove(struct device *dev) +{ + DBG("udc_au1xxx_drv_remove()\n"); + udc_drv_remove(dev); + return 0; +} + +static int udc_au1xxx_drv_suspend(struct device *dev, pm_message_t state) +{ + struct udc *udc_dev = dev_get_drvdata(dev); + return udc_suspend(udc_dev); +} + +static int udc_au1xxx_drv_resume(struct device *dev) +{ + struct udc *udc_dev = dev_get_drvdata(dev); + return udc_resume(udc_dev); +} + +static struct device_driver udc_au1xxx_driver = { + .name = "au1xxx-udc", + .bus = &platform_bus_type, + .probe = udc_au1xxx_drv_probe, + .remove = udc_au1xxx_drv_remove, + .suspend = udc_au1xxx_drv_suspend, + .resume = udc_au1xxx_drv_resume, +}; +#endif + + +/** + * Inits driver + * + * \return 0 if success + */ +static int __init init(void) +{ + int rc; + +#ifdef UDC_DEBUG + /* register char device */ + rc = register_chrdev(UDC_MAJOR_NUM, UDC_DEVICE_NAME, &udc_fops); + if (rc < 0) + { + printk("Error registering udc char device"); + } +#endif + +#ifdef CONFIG_SOC_AU1200 + /* probe by device system */ + rc = driver_register(&udc_au1xxx_driver); +#else + /* probe by PCI bus driver */ + rc = pci_module_init (&udc_pci_driver); +#endif + + return rc; +} +module_init (init); + +/** + * Cleans driver + */ +static void __exit cleanup(void) +{ +#ifdef UDC_DEBUG + /* unregister char device */ + unregister_chrdev(UDC_MAJOR_NUM, UDC_DEVICE_NAME); +#endif + +#ifdef CONFIG_SOC_AU1200 + /* unregister at device system */ + driver_unregister(&udc_au1xxx_driver); +#else + /* unregister at PCI bus driver */ + pci_unregister_driver (&udc_pci_driver); +#endif + +} +module_exit (cleanup); + diff --git a/drivers/usb/gadget/au1200udc.h b/drivers/usb/gadget/au1200udc.h new file mode 100644 index 0000000..d7d8491 --- /dev/null +++ b/drivers/usb/gadget/au1200udc.h @@ -0,0 +1,936 @@ +/* + * Header for driver for AMD Au1200 UDC high/full speed USB device controller + */ + +/* + * Copyright (C) 2005 AMD (http://www.amd.com) + * Author: Thomas Dahlmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef AU1200UDC_H +#define AU1200UDC_H + +/***************************************************************************** +* Constants +*****************************************************************************/ + +/* Driver constants -------------------------------------------------------*/ +#define DRIVER_NAME_FOR_PRINT "au1200udc" + +/* PCI constants -----------------------------------------------------------*/ +#define UDC_PCI_VENID 0x1022 +#define UDC_PCI_DEVID 0x2096 +#define UDC_PCI_CLASS ((PCI_CLASS_SERIAL_USB << 8) | 0xfe) +#define UDC_PCI_CLASS_MASK 0xffffffff + +/* Platform specific -------------------------------------------------------*/ +#if defined(CONFIG_SOC_AU1200) +#define UDC_PCIPOOL_ALIGN 32 +#define UDC_PCIPOOL_CROSS 4096 +#define UDC_PCIPOOL_GFP_STP (GFP_ATOMIC | GFP_DMA) +#else +#define UDC_PCIPOOL_ALIGN 0 +#define UDC_PCIPOOL_CROSS 0 +#define UDC_PCIPOOL_GFP_STP (GFP_KERNEL) +#endif + +/* temp define for AU1200, will live in au1000.h normally */ +#ifndef USB_UDC_BASE +#define USB_UDC_BASE 0x14022000 +#define USB_UDC_LEN 0x2000 +#define USB_MSR_BASE 0xB4020000 +#define USB_MSR_MCFG 4 +#define USBMSRMCFG_DMEMEN 4 +#define USBMSRMCFG_DBMEN 5 +#define USBMSRMCFG_UDCCLKEN 18 +#define USBMSRMCFG_PHYPLLEN 19 +#define USBMSRMCFG_RDCOMB 30 +#define USBMSRMCFG_PFEN 31 +#define AU1200_USB_INT 29 +#endif +#ifndef USBMSRMCFG_UCAM +#define USBMSRMCFG_UCAM 7 +#endif +#define USBMSRMCFG_DEFAULT 0x00d02000 + +/* other constants */ +#define UDC_RDE_TIMER_SECONDS 1 +#define UDC_RDE_TIMER_DIV 10 +#define UDC_POLLSTALL_TIMER_USECONDS 500 + +/* Special optimization for certain gadgets ------------------------------- */ +/* use hardware NAK if IN queue empty, this can be used to +* avoid interrupt flood as for the ethernet gadget where host side +* sends IN tokens permamently */ +#define UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE +/*#undef UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE*/ + +/* IP core defect/bug/case workarounds ---------------------------------------- */ +/* Hs AU1200 A0 workarounds */ +/* Description: "Last descriptor loop */ +#define UDC_IPDEFECT_9000004946_WORKAROUND +/* Description: "DMA machine not restartable" */ +#define UDC_IPCASE_8000018724_WORKAROUND +/* Description: "UDC not accessible when phy is suspended */ +#define UDC_IPBUG_3943_WORKAROUND +/* Description: "USB device needs session-valid status information */ +#define UDC_IPBUG_3950_WORKAROUND +/* Description: "Clearing endpoint NAK bits not possible if RxFIFO + not empty (PCS#1503) */ +#define UDC_IPBUG_3958_WORKAROUND +/* UDC data errors during loop test (DMA) + - found only with Au1200 but used for Hs too, bug entry + is in Au1200 bugzilla + - the workaround applies only for small packets */ +#define UDC_IPBUG_2253_WORKAROUND +#define UDC_SMALL_PACKET 32 + +/* Disable workarounds which don't apply to Hs AU1200 B1/ Au1200 A1 */ +/* And enable new features */ + +/* Hs AU1200 A0 flag */ +/* #define UDC_HSA0 */ +/* Hs AU1200 B1 flag */ +#define UDC_HSB1 + +/* Hs AU1200 chip rev. */ +#define UDC_HSA0_REV 1 +#define UDC_HSB1_REV 2 + +/* consistency */ +#ifdef UDC_HSB1 +#undef UDC_HSA0 +#endif + +/* Au1200 rev. */ +#if defined(CONFIG_SOC_AU1200) +#undef UDC_HSB1 +#undef UDC_HSA0 +/* revision value */ +#define UDC_AUAB_REV 0 +#define UDC_AUAC_REV 1 +#define UDC_AUA0 0 +/* Au1200 AC flag */ +/* #define UDC_AUA1 1 */ +#endif + +/* disable workarounds for newer chip revisions */ +#ifdef UDC_HSB1 +#undef UDC_IPBUG_3943_WORKAROUND +#undef UDC_IPBUG_3950_WORKAROUND +#define UDC_DMARST_AVAIL +#endif +#ifdef UDC_AUA1 +#undef UDC_IPBUG_3943_WORKAROUND +#endif + +/* SETUP usb commands +* needed, because some SETUP's are handled in hw, but must be passed to +* gadget driver above -----------------------------------------------------*/ +/* SET_CONFIG */ +#define UDC_SETCONFIG_DWORD0 0x00000900 +#define UDC_SETCONFIG_DWORD0_VALUE_MASK 0xffff0000 +#define UDC_SETCONFIG_DWORD0_VALUE_OFS 16 + +#define UDC_SETCONFIG_DWORD1 0x00000000 + +/* SET_INTERFACE */ +#define UDC_SETINTF_DWORD0 0x00000b00 +#define UDC_SETINTF_DWORD0_ALT_MASK 0xffff0000 +#define UDC_SETINTF_DWORD0_ALT_OFS 16 + +#define UDC_SETINTF_DWORD1 0x00000000 +#define UDC_SETINTF_DWORD1_INTF_MASK 0x0000ffff +#define UDC_SETINTF_DWORD1_INTF_OFS 0 + +/* Mass storage reset */ +#define UDC_MSCRES_DWORD0 0x0000ff21 +#define UDC_MSCRES_DWORD1 0x00000000 + +/* Global CSR's -------------------------------------------------------------*/ +/* UDC CSR's */ +#define UDC_CSR_ADDR 0x500 + +/* EP NE bits */ +/* EP number */ +#define UDC_CSR_NE_NUM_MASK 0x0000000f +#define UDC_CSR_NE_NUM_OFS 0 +/* EP direction */ +#define UDC_CSR_NE_DIR_MASK 0x00000010 +#define UDC_CSR_NE_DIR_OFS 4 +/* EP type */ +#define UDC_CSR_NE_TYPE_MASK 0x00000060 +#define UDC_CSR_NE_TYPE_OFS 5 +/* EP config number */ +#define UDC_CSR_NE_CFG_MASK 0x00000780 +#define UDC_CSR_NE_CFG_OFS 7 +/* EP interface number */ +#define UDC_CSR_NE_INTF_MASK 0x00007800 +#define UDC_CSR_NE_INTF_OFS 11 +/* EP alt setting */ +#define UDC_CSR_NE_ALT_MASK 0x00078000 +#define UDC_CSR_NE_ALT_OFS 15 + +/* max pkt */ +#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000 +#define UDC_CSR_NE_MAX_PKT_OFS 19 + +/* Device Config Register ---------------------------------------------------*/ +#define UDC_DEVCFG_ADDR 0x400 + +#define UDC_DEVCFG_SOFTRESET 31 +#define UDC_DEVCFG_HNPSFEN 30 +#define UDC_DEVCFG_DMARST 29 +#define UDC_DEVCFG_SET_DESC 18 +#define UDC_DEVCFG_CSR_PRG 17 +#define UDC_DEVCFG_STATUS 7 +#define UDC_DEVCFG_DIR 6 +#define UDC_DEVCFG_PI 5 +#define UDC_DEVCFG_SS 4 +#define UDC_DEVCFG_SP 3 +#define UDC_DEVCFG_RWKP 2 + +#define UDC_DEVCFG_SPD_MASK 0x3 +#define UDC_DEVCFG_SPD_OFS 0 +#define UDC_DEVCFG_SPD_HS 0x0 +#define UDC_DEVCFG_SPD_FS 0x1 +#define UDC_DEVCFG_SPD_LS 0x2 +/*#define UDC_DEVCFG_SPD_FS 0x3*/ + + +/* Device Control Register --------------------------------------------------*/ +#define UDC_DEVCTL_ADDR 0x404 + +#define UDC_DEVCTL_THLEN_MASK 0xff000000 +#define UDC_DEVCTL_THLEN_OFS 24 + +#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000 +#define UDC_DEVCTL_BRLEN_OFS 16 + +#define UDC_DEVCTL_CSR_DONE 13 +#define UDC_DEVCTL_DEVNAK 12 +#define UDC_DEVCTL_SD 10 +#define UDC_DEVCTL_MODE 9 +#define UDC_DEVCTL_BREN 8 +#define UDC_DEVCTL_THE 7 +#define UDC_DEVCTL_BF 6 +#define UDC_DEVCTL_BE 5 +#define UDC_DEVCTL_DU 4 +#define UDC_DEVCTL_TDE 3 +#define UDC_DEVCTL_RDE 2 +#define UDC_DEVCTL_RES 0 + + +/* Device Status Register ---------------------------------------------------*/ +#define UDC_DEVSTS_ADDR 0x408 + +#define UDC_DEVSTS_TS_MASK 0xfffc0000 +#define UDC_DEVSTS_TS_OFS 18 + +#define UDC_DEVSTS_SESSVLD 17 +#define UDC_DEVSTS_PHY_ERROR 16 +#define UDC_DEVSTS_RXFIFO_EMPTY 15 + +#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000 +#define UDC_DEVSTS_ENUM_SPEED_OFS 13 +#define UDC_DEVSTS_ENUM_SPEED_FULL 1 +#define UDC_DEVSTS_ENUM_SPEED_HIGH 0 + +#define UDC_DEVSTS_SUSP 12 + +#define UDC_DEVSTS_ALT_MASK 0x00000f00 +#define UDC_DEVSTS_ALT_OFS 8 + +#define UDC_DEVSTS_INTF_MASK 0x000000f0 +#define UDC_DEVSTS_INTF_OFS 4 + +#define UDC_DEVSTS_CFG_MASK 0x0000000f +#define UDC_DEVSTS_CFG_OFS 0 + + +/* Device Interrupt Register ------------------------------------------------*/ +#define UDC_DEVINT_ADDR 0x40c + +#define UDC_DEVINT_SVC 7 +#define UDC_DEVINT_ENUM 6 +#define UDC_DEVINT_SOF 5 +#define UDC_DEVINT_US 4 +#define UDC_DEVINT_UR 3 +#define UDC_DEVINT_ES 2 +#define UDC_DEVINT_SI 1 +#define UDC_DEVINT_SC 0 + +/* Device Interrupt Mask Register -------------------------------------------*/ +#define UDC_DEVINT_MSK_ADDR 0x410 + +#define UDC_DEVINT_MSK 0x7f + +/* Endpoint Interrupt Register ----------------------------------------------*/ +#define UDC_EPINT_ADDR 0x414 + +#define UDC_EPINT_OUT_MASK 0xffff0000 +#define UDC_EPINT_OUT_OFS 16 +#define UDC_EPINT_IN_MASK 0x0000ffff +#define UDC_EPINT_IN_OFS 0 + +#define UDC_EPINT_IN_EP0 0 +#define UDC_EPINT_IN_EP1 1 +#define UDC_EPINT_IN_EP2 2 +#define UDC_EPINT_IN_EP3 3 +#define UDC_EPINT_OUT_EP0 16 +#define UDC_EPINT_OUT_EP1 17 +#define UDC_EPINT_OUT_EP2 18 +#define UDC_EPINT_OUT_EP3 19 + +#define UDC_EPINT_EP0_ENABLE_MSK 0x001e001e + +/* Endpoint Interrupt Mask Register -----------------------------------------*/ +#define UDC_EPINT_MSK_ADDR 0x418 + +#define UDC_EPINT_OUT_MSK_MASK 0xffff0000 +#define UDC_EPINT_OUT_MSK_OFS 16 +#define UDC_EPINT_IN_MSK_MASK 0x0000ffff +#define UDC_EPINT_IN_MSK_OFS 0 + +#define UDC_EPINT_MSK_DISABLE_ALL 0xffffffff +/* mask non-EP0 endpoints */ +#define UDC_EPDATAINT_MSK_DISABLE 0xfffefffe +/* mask all dev interrupts */ +#define UDC_DEV_MSK_DISABLE 0x7f + +/* Endpoint-specific CSR's --------------------------------------------------*/ +/* Endpoint Control Registers -----------------------------------------------*/ +#define UDC_EPREGS_ADDR 0x0 +#define UDC_EPIN_REGS_ADDR 0x0 +#define UDC_EPOUT_REGS_ADDR 0x200 + +#define UDC_EPCTL_ADDR 0x0 + +#define UDC_EPCTL_RRDY 9 +#define UDC_EPCTL_CNAK 8 +#define UDC_EPCTL_SNAK 7 +#define UDC_EPCTL_NAK 6 + +#define UDC_EPCTL_ET_MASK 0x00000030 +#define UDC_EPCTL_ET_OFS 4 +#define UDC_EPCTL_ET_CONTROL 0 +#define UDC_EPCTL_ET_ISO 1 +#define UDC_EPCTL_ET_BULK 2 +#define UDC_EPCTL_ET_INTERRUPT 3 + +#define UDC_EPCTL_P 3 +#define UDC_EPCTL_SN 2 +#define UDC_EPCTL_F 1 +#define UDC_EPCTL_S 0 + +/* Endpoint Status Registers ------------------------------------------------*/ +#define UDC_EPSTS_ADDR 0x4 + +#define UDC_EPSTS_RX_PKT_SIZE_MASK 0x007ff800 +#define UDC_EPSTS_RX_PKT_SIZE_OFS 11 + +#define UDC_EPSTS_TDC 10 +#define UDC_EPSTS_HE 9 +#define UDC_EPSTS_BNA 7 +#define UDC_EPSTS_IN 6 + +#define UDC_EPSTS_OUT_MASK 0x00000030 +#define UDC_EPSTS_OUT_OFS 4 +#define UDC_EPSTS_OUT_DATA 1 +#define UDC_EPSTS_OUT_DATA_CLEAR 0x10 +#define UDC_EPSTS_OUT_SETUP 2 +#define UDC_EPSTS_OUT_SETUP_CLEAR 0x20 + +/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT Registers ------*/ +#define UDC_EPIN_BUFF_SIZE_ADDR 0x8 +#define UDC_EPOUT_FRAME_NUMBER_ADDR 0x8 + +#define UDC_EPIN_BUFF_SIZE_MASK 0x0000ffff +#define UDC_EPIN_BUFF_SIZE_OFS 0 +/* EP0in txfifo = 128 bytes*/ +#define UDC_EPIN0_BUFF_SIZE 32 +/* EP0in fullspeed txfifo = 128 bytes*/ +#define UDC_FS_EPIN0_BUFF_SIZE 32 + +/* fifo size mult = fifo size / max packet */ +#define UDC_EPIN_BUFF_SIZE_MULT 2 + +/* EPin data fifo size = 1024 bytes DOUBLE BUFFERING */ +#define UDC_EPIN_BUFF_SIZE 256 +/* EPin small INT data fifo size = 128 bytes */ +#define UDC_EPIN_SMALLINT_BUFF_SIZE 32 + +/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */ +#define UDC_FS_EPIN_BUFF_SIZE 32 + +#define UDC_EPOUT_FRAME_NUMBER_MASK 0x0000ffff +#define UDC_EPOUT_FRAME_NUMBER_OFS 0 + +/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/ +#define UDC_EPOUT_BUFF_SIZE_ADDR 0x0c +#define UDC_EP_MAX_PKT_SIZE_ADDR 0x0c + +#define UDC_EPOUT_BUFF_SIZE_MASK 0xffff0000 +#define UDC_EPOUT_BUFF_SIZE_OFS 16 +#define UDC_EP_MAX_PKT_SIZE_MASK 0x0000ffff +#define UDC_EP_MAX_PKT_SIZE_OFS 0 +/* EP0in max packet size = 64 bytes */ +#define UDC_EP0IN_MAX_PKT_SIZE 64 +/* EP0out max packet size = 64 bytes */ +#define UDC_EP0OUT_MAX_PKT_SIZE 64 +/* EP0in fullspeed max packet size = 64 bytes */ +#define UDC_FS_EP0IN_MAX_PKT_SIZE 64 +/* EP0out fullspeed max packet size = 64 bytes */ +#define UDC_FS_EP0OUT_MAX_PKT_SIZE 64 + +/* Endpoint dma descriptors ------------------------------------------------*/ +/* Setup data */ +/* Status dword */ +#define UDC_DMA_STP_STS_CFG_MASK 0x0fff0000 +#define UDC_DMA_STP_STS_CFG_OFS 16 +#define UDC_DMA_STP_STS_CFG_ALT_MASK 0x000f0000 +#define UDC_DMA_STP_STS_CFG_ALT_OFS 16 +#define UDC_DMA_STP_STS_CFG_INTF_MASK 0x00f00000 +#define UDC_DMA_STP_STS_CFG_INTF_OFS 20 +#define UDC_DMA_STP_STS_CFG_NUM_MASK 0x0f000000 +#define UDC_DMA_STP_STS_CFG_NUM_OFS 24 +#define UDC_DMA_STP_STS_RX_MASK 0x30000000 +#define UDC_DMA_STP_STS_RX_OFS 28 +#define UDC_DMA_STP_STS_BS_MASK 0xc0000000 +#define UDC_DMA_STP_STS_BS_OFS 30 +#define UDC_DMA_STP_STS_BS_HOST_READY 0 +#define UDC_DMA_STP_STS_BS_DMA_BUSY 1 +#define UDC_DMA_STP_STS_BS_DMA_DONE 2 +#define UDC_DMA_STP_STS_BS_HOST_BUSY 3 +/* IN data */ +/* Status dword */ +#define UDC_DMA_IN_STS_TXBYTES_MASK 0x0000ffff +#define UDC_DMA_IN_STS_TXBYTES_OFS 0 +#define UDC_DMA_IN_STS_FRAMENUM_MASK 0x07ff0000 +#define UDC_DMA_IN_STS_FRAMENUM_OFS 0 +#define UDC_DMA_IN_STS_L 27 +#define UDC_DMA_IN_STS_TX_MASK 0x30000000 +#define UDC_DMA_IN_STS_TX_OFS 28 +#define UDC_DMA_IN_STS_BS_MASK 0xc0000000 +#define UDC_DMA_IN_STS_BS_OFS 30 +#define UDC_DMA_IN_STS_BS_HOST_READY 0 +#define UDC_DMA_IN_STS_BS_DMA_BUSY 1 +#define UDC_DMA_IN_STS_BS_DMA_DONE 2 +#define UDC_DMA_IN_STS_BS_HOST_BUSY 3 +/* OUT data */ +/* Status dword */ +#define UDC_DMA_OUT_STS_RXBYTES_MASK 0x0000ffff +#define UDC_DMA_OUT_STS_RXBYTES_OFS 0 +#define UDC_DMA_OUT_STS_FRAMENUM_MASK 0x07ff0000 +#define UDC_DMA_OUT_STS_FRAMENUM_OFS 0 +#define UDC_DMA_OUT_STS_L 27 +#define UDC_DMA_OUT_STS_RX_MASK 0x30000000 +#define UDC_DMA_OUT_STS_RX_OFS 28 +#define UDC_DMA_OUT_STS_BS_MASK 0xc0000000 +#define UDC_DMA_OUT_STS_BS_OFS 30 +#define UDC_DMA_OUT_STS_BS_HOST_READY 0 +#define UDC_DMA_OUT_STS_BS_DMA_BUSY 1 +#define UDC_DMA_OUT_STS_BS_DMA_DONE 2 +#define UDC_DMA_OUT_STS_BS_HOST_BUSY 3 +/* other constants */ +/* max ep0in packet */ +#define UDC_EP0IN_MAXPACKET 1000 +/* max dma packet */ +#define UDC_DMA_MAXPACKET 65536 +/* DMA buffer len for temp request, should be the same as the upper +layer gadget is using */ +#define UDC_DMA_TEMP_BUFFER_LEN 4096 +/* un-usable DMA address */ +#define DMA_DONT_USE (~(dma_addr_t) 0 ) + +/* other Endpoint register addresses and values-----------------------------*/ +#define UDC_EP_SUBPTR_ADDR 0x10 +#define UDC_EP_DESPTR_ADDR 0x14 +#define UDC_EP_WRITE_CONFIRM_ADDR 0x1c + +/* EP number as layouted in AHB space */ +#define UDC_EP_NUM 32 +#define UDC_EPIN_NUM 16 +#define UDC_EPIN_NUM_USED 5 +#define UDC_EPOUT_NUM 16 +/* EP number of EP's really used = EP0 + 8 data EP's */ +#define UDC_USED_EP_NUM 9 +/* UDC CSR regs are aligned but AHB regs not - offset for OUT EP's */ +#define UDC_CSR_EP_OUT_IX_OFS 12 + +#define UDC_EP0OUT_IX 16 +#define UDC_EP0IN_IX 0 + +/* max packet */ +#define UDC_HS_BULK_MAXPKT 512 + +/* Rx fifo address and size = 1k -------------------------------------------*/ +#define UDC_RXFIFO_ADDR 0x800 +#define UDC_RXFIFO_SIZE 0x400 + +/* Tx fifo address and size = 1.5k -----------------------------------------*/ +#define UDC_TXFIFO_ADDR 0xc00 +#define UDC_TXFIFO_SIZE 0x600 + +/* default data endpoints --------------------------------------------------*/ +#define UDC_EPIN_STATUS_IX 1 +#define UDC_EPIN_IX 2 +#define UDC_EPOUT_IX 18 + +/* general constants -------------------------------------------------------*/ +#define UDC_DWORD_BYTES 4 +#define UDC_BITS_PER_BYTE_SHIFT 3 +#define UDC_BYTE_MASK 0xff +#define UDC_BITS_PER_BYTE 8 + +/* char device constants ---------------------------------------------------*/ +/* names */ +#ifdef UDC_DEBUG +#ifdef UDC_DRIVER_NAME +#define UDC_DEVICE_NAME UDC_DRIVER_NAME +#else +#define UDC_DEVICE_NAME "au1200udc" +#endif +#define UDC_DEVICE_FILE_NAME "au1200udc_dev" +#define UDC_DEVICE_FILE_INODE "/dev/au1200udc_dev" +/* major number */ +#define UDC_MAJOR_NUM 240 +#endif + +#ifdef __KERNEL__ +/* kernel wrappers */ +#define device_create_file(x,y) do {} while (0) +#define device_remove_file device_create_file + +#ifndef WARN_ON +#define WARN_ON(a) do {} while (0) +#endif + +#ifndef BUG_ON +#define BUG_ON(cond)do {if (unlikely((cond) != 0)) BUG();} while(0) +#endif + +#ifndef likely +#define likely(a) (a) +#define unlikely(a) (a) +#endif + +#ifndef container_of +#define container_of list_entry +#endif + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_HANDLED +#define IRQ_NONE +#define IRQ_RETVAL(a) +#endif +#endif + +/* MIPS specific -----------------------------------------------------------*/ +#if defined(CONFIG_SOC_AU1200) +/* sync does it without refilling the pipe, + so read after write can bypass write */ +#define au1200_sync_delay() {\ + asm(".set noreorder"); \ + asm("b l1");\ + asm("l1:");\ + asm("sync");\ + asm("nop");\ + asm("nop");\ + asm("nop");\ + asm("nop");\ + asm("b l2");\ + asm("l2:");\ + asm("nop");\ + asm("nop");\ + asm(".set reorder");\ +} + +/* #define wb_flush() au1200_sync() */ +#define wb_flush() __asm__ volatile ("sync") + +/* special write functions for MIPS (with write buffer flush) */ +#undef writel +#undef writeb +#define writel(d,a) {((*(volatile unsigned int *)(a))=(d));wb_flush();} +#define writeb(d,a) {((*(volatile unsigned char *)(a))=(d));wb_flush();} +#else +#define wb_flush() {} +#endif + +/***************************************************************************** +* Includes +*****************************************************************************/ + +#if !defined(UDC_HSB1) +#include "au1200otg.h" +#endif + +/***************************************************************************** +* Types +*****************************************************************************/ + +/* UDC CSR's */ +struct udc_csrs { + + /* sca - setup command address */ + u32 sca; + + /* ep ne's */ + u32 ne[UDC_USED_EP_NUM]; +} __attribute__ ((packed)); + +/* AHB subsystem CSR registers */ +struct udc_regs { + + /* device configuration */ + u32 cfg; + + /* device control */ + u32 ctl; + + /* device status */ + u32 sts; + + /* device interrupt */ + u32 irqsts; + + /* device interrupt mask */ + u32 irqmsk; + + /* endpoint interrupt */ + u32 ep_irqsts; + + /* endpoint interrupt mask */ + u32 ep_irqmsk; +} __attribute__ ((packed)); + +/* endpoint specific registers */ +struct udc_ep_regs { + + /* endpoint control */ + u32 ctl; + + /* endpoint status */ + u32 sts; + + /* endpoint buffer size in/ receive packet frame number out */ + u32 bufin_framenum; + + /* endpoint buffer size out/max packet size */ + u32 bufout_maxpkt; + + /* endpoint setup buffer pointer */ + u32 subptr; + + /* endpoint data descriptor pointer */ + u32 desptr; + + /* reserverd */ + u32 reserved; + + /* write/read confirmation */ + u32 confirm; + +} __attribute__ ((packed)); + +#ifdef __KERNEL__ +/* control data DMA desc */ +struct udc_stp_dma { + /* status quadlet */ + u32 status; + /* reserved */ + u32 _reserved; + /* first setup word */ + u32 data12; + /* second setup word */ + u32 data34; +} __attribute__ ((aligned (16))); + +/* normal data DMA desc */ +struct udc_data_dma { + /* status quadlet */ + u32 status; + /* reserved */ + u32 _reserved; + /* buffer pointer */ + u32 bufptr; + /* next descriptor pointer */ + u32 next; +} __attribute__ ((aligned (16))); + +/* request packet */ +struct udc_request { + /* embedded gadget ep */ + struct usb_request req; + + /* flags */ + unsigned dma_going : 1, + dma_mapping : 1, + dma_done : 1; + /* phys. address */ + dma_addr_t td_phys; + /* first dma desc. of chain */ + struct udc_data_dma *td_data; + /* last dma desc. of chain */ + struct udc_data_dma *td_data_last; +#ifdef UDC_IPDEFECT_9000004946_WORKAROUND + /* next pointer of broken chain */ + dma_addr_t td_data_last_next; +#endif + + struct list_head queue; + + /* chain length */ + unsigned chain_len; + +}; + +/* UDC specific endpoint parameters */ +struct udc_ep { + struct usb_ep ep; + struct udc_ep_regs *regs; + u32 *txfifo; + u32* dma; + dma_addr_t td_phys; + dma_addr_t td_stp_dma; + struct udc_stp_dma *td_stp; + struct udc_data_dma *td; + /* temp request */ + struct udc_request *req; + unsigned req_used; + unsigned req_completed; + /* NAK state */ + unsigned naking; + + struct udc *dev; + + /* queue for requests */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned halted; + unsigned num : 5, + fifo_depth : 14, + in : 1; +}; + +/* device struct */ +struct udc { + struct usb_gadget gadget; + spinlock_t lock; + /* all endpoints */ + struct udc_ep ep[UDC_EP_NUM]; + struct usb_gadget_driver *driver; + struct otg_transceiver *otg_transceiver; +#if !defined(UDC_HSB1) + struct usb_otg_gadget_extension *otg_driver; +#endif + /* operational flags */ + unsigned active : 1, + stall_ep0in : 1, + waiting_zlp_ack_ep0in : 1, + set_cfg_not_acked : 1, + irq_registered : 1, + otg_supported : 1, + data_ep_enabled : 1, + data_ep_queued : 1, + mem_region : 1, + selfpowered : 1, + sys_suspended : 1, + connected; + + u16 chiprev; + + /* registers */ +#if defined(CONFIG_SOC_AU1200) && !defined(USE_AU1200_PCI_DUMMY) + struct device *pdev; +#else + struct pci_dev *pdev; +#endif + struct udc_csrs *csr; + struct udc_regs *regs; + struct udc_ep_regs *ep_regs; + u32* rxfifo; + u32* txfifo; + + /* DMA desc pools */ +#if defined(CONFIG_SOC_AU1200) + struct dma_pool *data_requests; + struct dma_pool *stp_requests; +#else + struct pci_pool *data_requests; + struct pci_pool *stp_requests; +#endif + + /* device data */ + unsigned long phys_addr; + void* virt_addr; + unsigned irq; + + /* states */ + u16 cur_config; + u16 cur_intf; + u16 cur_alt; +}; + +/* setup request data */ +union udc_setup_data { + u32 data[2]; + struct usb_ctrlrequest request; +}; +#endif /*__KERNEL__*/ + +/***************************************************************************** +* Macros +*****************************************************************************/ + +/*************************************** +* SET and GET bitfields in u32 values +* via constants for mask/offset: +* is the text between +* UDC_ and _MASK|_OFS of appropiate +* constant +****************************************/ +/* set bitfield value in u32 u32Val */ +#define AMD_ADDBITS(u32Val, bitfield_val, bitfield_stub_name)\ + (((u32Val) & (((u32) ~((u32) bitfield_stub_name##_MASK))))\ + |(((bitfield_val) << ((u32) bitfield_stub_name##_OFS))\ + & ((u32) bitfield_stub_name##_MASK))) + +/* set bitfield value in zero-initialized u32 u32Val */ +/* => bitfield bits in u32Val are all zero */ +#define AMD_INIT_SETBITS(u32Val, bitfield_val, bitfield_stub_name)\ + ((u32Val)\ + |(((bitfield_val) << ((u32) bitfield_stub_name##_OFS))\ + &((u32) bitfield_stub_name##_MASK))) + +/* get bitfield value from u32 u32Val */ +#define AMD_GETBITS(u32Val, bitfield_stub_name)\ + ((u32Val & ((u32) bitfield_stub_name##_MASK))\ + >> ((u32) bitfield_stub_name##_OFS)) + +/* SET and GET bits in u32 values ------------------------------------------*/ +#define AMD_BIT(bit_stub_name) (1 << bit_stub_name) +#define AMD_UNMASK_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name)) +#define AMD_CLEAR_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name)) + +/* misc --------------------------------------------------------------------*/ +#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") + +/* UDC specific macros -----------------------------------------------------*/ +#ifdef UDC_DMARST_AVAIL +#define UDC_DMARST(tmp, dev) \ + DBG("DMA machine reset\n"); \ + tmp = readl(&dev->regs->cfg); \ + writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg); \ + writel(tmp, &dev->regs->cfg); +#else +#define UDC_DMARST(tmp, dev) {} +#endif + +/* print macros ------------------------------------------------------------*/ + +#ifdef UDC_VERBOSE +#ifndef UDC_DEBUG +#define UDC_DEBUG +#endif +#endif + +/** + * \brief + * Macro for printing information in drivers + * + * This macro is used for printing kernel messages in driver source code. + * It should be used for printing useful information about states and called + * functions for normal operation (not for errors and warnings). + * + * \param fmt is format string for printk + * \param args... are arguments given to printk (number depends on ) + * \return code from printk + */ +#define INFO(args...) \ + printk(KERN_INFO DRIVER_NAME_FOR_PRINT ": " args) + +/** + * \brief + * Macro for printing warnings in drivers + * + * This macro is used for printing kernel messages in driver source code. + * It should be used for printing warnings. + * + * \param fmt is format string for printk + * \param args... are arguments given to printk (number depends on ) + * \return code from printk + */ +#define WARN(args...) \ + printk(KERN_WARNING DRIVER_NAME_FOR_PRINT " warning: " args) + +/** + * \brief + * Macro for printing errors in drivers + * + * This macro is used for printing kernel messages in driver source code. + * It should be used for printing errors. + * + * \param fmt is format string for printk + * \param args... are arguments given to printk (number depends on ) + * \return code from printk + */ +#define ERR(args...) \ + printk(KERN_ERR DRIVER_NAME_FOR_PRINT " error: " args) + +/** + * \brief + * Macro for printing debug messages in drivers + * + * This macro is used for printing kernel messages in driver source code + * when UDC_DEBUG is defined + * It should be used for printing debug messages. + * + * \param fmt is format string for printk + * \param args... are arguments given to printk (number depends on ) + * \return code from printk + */ +#ifdef UDC_DEBUG +#define DBG(args...) \ + printk(KERN_DEBUG DRIVER_NAME_FOR_PRINT " debug: " args) +#else + +#define DBG(args...) \ + do {} while (0) +#endif + +/** + * \brief + * Macro for printing verbose debug messages in drivers + * + * This macro is used for printing kernel messages in driver source code + * when UDC_DEBUG and UDC_VERBOSE is defined + * It should be used for printing debug messages. + * + * \param fmt is format string for printk + * \param args... are arguments given to printk (number depends on ) + * \return code from printk + */ +#ifdef UDC_VERBOSE +#define VDBG DBG +#else +#define VDBG(args...) \ + do {} while (0) +#endif +#endif /* #ifdef AU1200UDC_H */ diff --git a/drivers/usb/gadget/au1200uoc.c b/drivers/usb/gadget/au1200uoc.c new file mode 100644 index 0000000..5297eee --- /dev/null +++ b/drivers/usb/gadget/au1200uoc.c @@ -0,0 +1,2015 @@ +/* + * AMD Au1200 USB OTG controller driver + */ + +/* + * Copyright (C) 2005 AMD (http://www.amd.com) + * Author: Karsten Boge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + */ + + +/***************************************************************************** + * Includes + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE +#ifndef CONFIG_USB_OTG +#define CONFIG_USB_OTG +#endif +#endif + +/* #define DEBUG */ +/* #define VERBOSE */ + +#ifdef CONFIG_SOC_AU1200 +#include +#include +#include +#endif + +#include +#include +#include + +#define DRIVER_DESC "AMD Au1200 USB OTG Controller" +#define DRIVER_VERSION "01.00.0201 - $Revision: #3 $" +#define DRIVER_NAME_FOR_PRINT OTG_DRIVER_NAME + +#include "au1200otg.h" +#include "au1200uoc.h" + + +/***************************************************************************** + * Function Declarations + *****************************************************************************/ + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE +static int otg_test_bind(struct otg **); +static int otg_test_unbind(struct otg **); +#endif +#ifdef CONFIG_USB_OTG +static int otg_app_request(u32); +static int otg1200_set_host (struct otg_transceiver *, struct usb_bus *); +static int otg1200_set_power (struct otg_transceiver *, unsigned); +static int otg1200_start_srp (struct otg_transceiver *); +static int otg1200_start_hnp (struct otg_transceiver *); +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) +static u32 otg_app_query(int); +static int otg1200_set_peripheral (struct otg_transceiver *, + struct usb_gadget *); +#endif + + +/***************************************************************************** + * Data + *****************************************************************************/ + +static const char driver_name [] = OTG_DRIVER_NAME; +static const char driver_desc [] = DRIVER_DESC; + +static struct otg *the_controller; +static const char *transceiver_label = "au1200_otg"; + +static u32 init_state = 0; +#ifdef OTG_HNP_ERR_LOG +int hnp_err = 0; +#endif + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE +static struct usb_otg_test_extension otg_test_extension = { + .bind = otg_test_bind, + .unbind = otg_test_unbind, + .request = otg_app_request, + .query = otg_app_query +}; +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + +struct usb_otg_gadget_extension otg_gadget_extension = { +#ifdef CONFIG_USB_OTG + .request = otg_app_request, +#else + .request = NULL, +#endif + .query = otg_app_query, + .notify = NULL +}; + +static u32 state_mask; +u32 otg_tmr_high_count = 0; +#endif + +#ifdef CONFIG_SOC_AU1200 +#ifdef USE_AU1200_PCI_DUMMY +static struct pci_dev pdev_dummy; +#endif +#endif + + +/***************************************************************************** + * Function Definitions + *****************************************************************************/ + +/** + * \brief + * fill OTG transceiver struct + * + * \param transceiver OTG transceiver + * + * \return void + */ +static inline void otg_init_transceiver (struct otg_transceiver *transceiver) +{ + transceiver_to_otg(transceiver)->prv_state = OTG_STATE_UNDEFINED; + transceiver_to_otg(transceiver)->params = 0; + transceiver_to_otg(transceiver)->hostcount = 0; + transceiver_to_otg(transceiver)->otg_priv = (void *) &otg_gadget_extension; + + transceiver->dev = NULL; + transceiver->label = transceiver_label; + transceiver->default_a = 0; + transceiver->state = OTG_STATE_UNDEFINED; + transceiver->host = NULL; +#ifdef CONFIG_USB_OTG_HIGHSPEED + transceiver->companion = NULL; +#endif + transceiver->gadget = NULL; + transceiver->port_status = 0; + transceiver->port_change = 0; +#ifdef CONFIG_USB_OTG + transceiver->set_host = otg1200_set_host; + transceiver->set_peripheral = otg1200_set_peripheral; + transceiver->set_power = otg1200_set_power; + transceiver->start_srp = otg1200_start_srp; + transceiver->start_hnp = otg1200_start_hnp; +#else + transceiver->set_host = NULL; + transceiver->set_peripheral = otg1200_set_peripheral; + transceiver->set_power = NULL; + transceiver->start_srp = NULL; + transceiver->start_hnp = NULL; +#endif +} + +#ifdef CONFIG_USB_OTGMUX_GADGET +/** + * \brief + * OTG state change + * + * subset of OTG states to support the gadget only or + * ID pin configuration + * + * \param otg OTG controller info + * \param event_code event that requested a state change + * \param pEvt_mask + * + * \return events that were not handled here + */ +u32 otg_change_state (struct otg *otg, u32 _event, u32 *pEvt_mask) +{ + u32 event_code = _event; + u32 uoc_status = get_status (otg); + + if (GOT_EVENT (OTG_GADGET_READY, event_code) + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + || GOT_EVENT (OTG_HOST_READY, event_code) +#endif + ) { + + if (((IS_FLAG_RES (otg, OTG_GADGET_READY)) + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + || (IS_FLAG_RES (otg, OTG_HOST_READY)) +#endif + ) + + && ((otg->transceiver.state & OTG_STATE_MASK) != + OTG_STATE_UNDEFINED)) { + + /* driver unload, back to "not ready" state */ + + CHANGE_STATE (otg, OTG_STATE_UNDEFINED, pEvt_mask); + RES_EVENT ((OTG_GADGET_READY | OTG_HOST_READY), + event_code); + } + } + if ((OTG_INT_TMX & event_code) && otg_tmr_high_count) { + + /* a long timer is running : decrement the high part */ + + restart_timer (otg); + otg_tmr_high_count--; + RES_EVENT (OTG_INT_TMX, event_code); + } + + do switch (otg->transceiver.state & OTG_STATE_MASK) { + + /* NOT_ASSIGNED (yet): init state, 1st time after loading */ + /* ====================================================== */ + + case OTG_STATE_UNDEFINED : + + CHECK_STATE (otg, OTG_STATE_UNDEFINED, pEvt_mask); + + if (IS_FLAG_RES (otg, OTG_FLAGS_ACTIV)) { + + /* seems to be the first time: let it run ! */ + + SET_FLAG (otg, OTG_FLAGS_ACTIV); + } + + /* muxer is still neutral */ + + RES_EVENT ((OTG_GADGET_READY | OTG_HOST_READY | + OTG_INT_IDC | OTG_INT_TMX), event_code); + + if ((IS_FLAG_SET (otg, OTG_GADGET_READY)) + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + || (IS_FLAG_SET (otg, OTG_HOST_READY)) +#endif + ) { + + /* NOT_READY (substate): drivers now ready */ + /* ======================================= */ + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + if (IS_BIT_RES (OTG_STS_ID, uoc_status)) { + + /* ID pin connected: A-device (host) */ + + CHANGE_STATE (otg, OTG_STATE_A_HOST, + pEvt_mask); + } + else { + /* ID pin not connected: disable (neutral) */ +#endif + CHANGE_STATE (otg, OTG_STATE_B_IDLE, + pEvt_mask); + +#ifdef CONFIG_USB_OTGMUX_IDPIN + } +#endif + } + break; + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + /* A: ID pin is connected, A-device drives VBus */ + /* ============================================ */ + + case OTG_STATE_A_HOST : + + CHECK_STATE (otg, OTG_STATE_A_HOST, pEvt_mask); + + if (IS_BIT_SET (OTG_STS_ID, uoc_status)) { + + /* ID change => B_IDLE */ + + RES_EVENT ((OTG_INT_IDC | OTG_INT_PCC), event_code); + CHANGE_STATE (otg, OTG_STATE_B_IDLE, pEvt_mask); + } + break; + + /* B: ID pin is not connected */ + /* ========================== */ +#endif + + case OTG_STATE_B_IDLE : + + /* B_IDLE: init state for B-devices */ + /* monitor VBus, no connection, no activity */ + + CHECK_STATE (otg, OTG_STATE_B_IDLE, pEvt_mask); + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + if (IS_BIT_RES (OTG_STS_ID, uoc_status)) { + + /* ID pin changed => A_HOST */ + + RES_EVENT (OTG_INT_IDC, event_code); + CHANGE_STATE (otg, OTG_STATE_A_HOST, pEvt_mask); + } + else if (IS_BIT_SET (OTG_STS_SESSVLD, uoc_status)) +#else + if (IS_BIT_SET (OTG_STS_SESSVLD, uoc_status)) +#endif + { + /* Session valid => B_PERIPHERAL */ + + RES_EVENT (OTG_INT_SVC, event_code); + CHANGE_STATE (otg, OTG_STATE_B_PERIPHERAL, pEvt_mask); +#ifndef CONFIG_TEST_MODE_ONLY + if (otg_gadget_extension.notify) { + otg_gadget_extension.notify ( + OTG_GADGET_EVT_SVALID); + } +#endif + } + break; + + case OTG_STATE_B_PERIPHERAL : + + /* B_PERIPHERAL: connected to A-host, responding */ + /* VBus driven by A, remote activity */ + + CHECK_STATE (otg, OTG_STATE_B_PERIPHERAL, pEvt_mask); + +#ifdef CONFIG_USB_OTGMUX_IDPIN + + if (IS_BIT_RES (OTG_STS_ID, uoc_status) || + IS_BIT_RES (OTG_STS_SESSVLD, uoc_status)) +#else + if (IS_BIT_RES (OTG_STS_SESSVLD, uoc_status)) +#endif + { + /* ID pin changed | ~Session valid => B_IDLE */ + + RES_EVENT ((OTG_INT_IDC | OTG_INT_SVC), event_code); + CHANGE_STATE (otg, OTG_STATE_B_IDLE, pEvt_mask); +#ifndef CONFIG_TEST_MODE_ONLY + if (otg_gadget_extension.notify) { + otg_gadget_extension.notify ( + OTG_GADGET_EVT_SVDROP); + } +#endif + } + break; + + /* unlikely */ default : + + /* something went wrong */ + + BUG (); + break; + } + while ((otg->transceiver.state ^ otg->prv_state) & + OTG_STATE_MASK); + + DBG ("OTG-state change done\n"); + + return event_code; +} +#endif /* CONFIG_USB_OTGMUX_GADGET */ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + +/** + * \brief + * OTG state change request + * + * \param dev OTG device info + * \param params + * + * \return void + */ +static inline void otg_req_state_chg (struct otg *otg, u32 params) +{ + u32 temp, tmp2, flags; + + local_irq_save (flags); + + /* disable global OTG interrupt, clear int status: */ + temp = ~((u32) OTG_INT_GLOBAL) & readl (&otg->regs->inten); + writel (temp, &otg->regs->inten); + tmp2 = readl (&otg->regs->intr); + writel (tmp2, &otg->regs->intr); + temp &= tmp2; + + /* update OTG state: */ + otg_change_state (otg, (params | temp), &temp); + + /* enable global OTG interrupt: */ +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3950: gadget needs disconnect notification ***/ +#ifdef UDC_HSA0_WORKAROUNDS + state_mask = ~temp & OTG_INT_ADDS; + writel ((OTG_INT_ADDS | temp | OTG_INT_GLOBAL), &otg->regs->inten); +#else + writel ((temp | OTG_INT_GLOBAL), &otg->regs->inten); +#endif + local_irq_restore (flags); +} +#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE + +/** + * \brief + * Bind the OTG controller to the test driver + * + * \param dev otg controller info + * + * \return error code + */ +static int otg_test_bind (struct otg **otg) +{ + *otg = the_controller; + if (!*otg) + return -EINVAL; +#ifdef CONFIG_TEST_MODE_ONLY + INFO ("bind OTG driver to the test driver, enable device\n"); + SET_FLAG (*otg, OTG_HOST_READY | OTG_GADGET_READY); + if (IS_FLAG_SET (*otg, OTG_FLAGS_ACTIV)) { + otg_req_state_chg (*otg, OTG_HOST_READY | OTG_GADGET_READY); + } + (*otg)->transceiver.gadget = (struct usb_gadget *) 1; +#else + INFO ("bind OTG driver to the test interface\n"); +#endif + return 0; +} + +/** + * \brief + * Unbind the OTG controller from the test driver + * + * \param dev otg controller info + * + * \return error code + */ +static int otg_test_unbind (struct otg **otg) +{ +#ifdef CONFIG_TEST_MODE_ONLY + INFO ("unbind OTG driver from the test driver, disable device\n"); + (*otg)->transceiver.gadget = NULL; + RES_FLAG (*otg, OTG_HOST_READY | OTG_GADGET_READY); + if (IS_FLAG_SET (*otg, OTG_FLAGS_ACTIV)) { + otg_req_state_chg (*otg, OTG_HOST_READY | OTG_GADGET_READY); + } + *otg = NULL; + return otg_unbind_gadget (); +#else + INFO ("unbind OTG driver from the test interface\n"); + *otg = NULL; + return 0; +#endif +} +#endif /* CONFIG_USB_OTG_TEST_INTERFACE */ + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + +/** + * \brief + * OTG set transceiver: + * + * \param pointer to transceiver struct + * + * \return void + */ +int otg_set_transceiver (struct otg_transceiver *transceiver) +{ + struct otg *otg = the_controller; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -EINVAL; + } + else { + return 0; + } +} + +/** + * \brief + * OTG get transceiver: provide info to others + * + * \param void + * + * \return pointer to transceiver struct + */ +struct otg_transceiver * otg_get_transceiver (void) +{ + return otg_to_transceiver (the_controller); +} +#endif + +#ifdef CONFIG_USB_OTG + +/** + * \brief + * Bind/unbind the OTG controller to/from usbcore + * + * \param transceiver this transceiver + * \param bus usb bus the host is assigned to + * + * \return error code + */ +static int otg1200_set_host (struct otg_transceiver *transceiver, + struct usb_bus *bus) +{ + struct otg *otg = the_controller; + int flag, count = 0; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -EINVAL; + } + if (bus) { + + /* This bus supports OTG */ + + if (transceiver->host) { + count++; + } +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (transceiver->companion) { + count++; + } + /* Now checking consistence ... */ + /* Depending on the driver loading sequence is possible */ + /* that the "Load state defaults" function was already */ + /* called so the state could be inconsistent. */ + if (transceiver->host && transceiver->companion) { + flag = (transceiver->host->is_b_host | + transceiver->companion->is_b_host); + if (transceiver->gadget && + !transceiver->gadget->is_a_peripheral) { + flag |= 1; + } + transceiver->host->is_b_host = flag; + transceiver->companion->is_b_host = flag; + } +#endif + DBG ("USB OTG: bind host: bus %d, port %d (OTG host #%d)\n", + bus->busnum, bus->otg_port, count); + if (IS_FLAG_RES (otg, OTG_HOST_READY)) { + RES_FLAG (otg, OTG_A_BUS_DROP); + SET_FLAG (otg, OTG_HOST_READY | OTG_A_BUS_REQ); + if (IS_FLAG_SET (otg, OTG_FLAGS_ACTIV)) { + otg_req_state_chg (otg, OTG_HOST_READY); + } + } + } + else { +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (transceiver->companion) { + count++; + } +#endif + if (transceiver->host) { + count++; + } + DBG ("USB OTG: unbind host (actual OTG hosts: %d)\n", count); + if (!count) { + RES_FLAG (otg, OTG_HOST_READY | OTG_A_BUS_REQ); + SET_FLAG (otg, OTG_A_BUS_DROP); + if (IS_FLAG_SET (otg, OTG_FLAGS_ACTIV)) { + otg_req_state_chg (otg, OTG_HOST_READY); + } + } + } + return 0; +} +#endif /* CONFIG_USB_OTG */ + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + +/** + * \brief + * Bind/unbind the OTG controller to/from usb gadget + * + * \param transceiver this transceiver + * \param gadget usb gadget info + * + * \return error code + */ +static int otg1200_set_peripheral (struct otg_transceiver *transceiver, + struct usb_gadget *gadget) +{ + struct otg *otg = the_controller; + int flag = 0; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -EINVAL; + } + if (gadget) { + if (transceiver->gadget) { + ERR ("USB gadget: OTG driver already registered\n"); + return -EBUSY; + } + DBG ("bind OTG driver to USB gadget\n"); + transceiver->gadget = gadget; + SET_FLAG (otg, OTG_GADGET_READY | OTG_B_BUS_REQ); + + /* Now checking consistence ... */ + /* Depending on the driver loading sequence is possible */ + /* that the "Load state defaults" function was already */ + /* called so the state could be inconsistent. */ +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (transceiver->companion && + !transceiver->companion->is_b_host) { + flag |= 1; + } +#endif + if (transceiver->host && !transceiver->host->is_b_host) { + flag |= 1; + } + transceiver->gadget->is_a_peripheral = flag; + + if (IS_FLAG_SET (otg, OTG_FLAGS_ACTIV)) { + otg_req_state_chg (otg, OTG_GADGET_READY); + } + if (IS_BIT_SET (OTG_STS_SESSVLD, readl (&otg->regs->sts)) && + (otg->transceiver.state & OTG_STATE_MASK) + == OTG_STATE_B_PERIPHERAL) { + VDBG ("calling gadget: connect\n"); + if (otg_gadget_extension.notify) { + otg_gadget_extension.notify ( + OTG_GADGET_EVT_SVALID); + } + } + return 0; + } + else { + DBG ("unbind OTG driver from USB gadget\n"); + RES_FLAG (otg, OTG_GADGET_READY | OTG_B_BUS_REQ); + if (IS_FLAG_SET (otg, OTG_FLAGS_ACTIV)) { + otg_req_state_chg (otg, OTG_GADGET_READY); + } + transceiver->gadget = NULL; + return 0; + } +} +#endif /* CONFIG_USB_OTGMUX || CONFIG_USB_OTGMUX_GADGET */ + +#ifdef CONFIG_USB_OTG + +/** + * \brief + * Set host controllers power budget + * + * \param transceiver this transceiver + * \param mA value to fill in + * + * \return error code + */ +static int otg1200_set_power (struct otg_transceiver *transceiver, + unsigned mA) +{ + struct otg *otg = the_controller; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -EINVAL; + } + /* TODO kboge: do we need to support this ? */ + + DBG ("USB OTG: power budget change not supported\n"); + return 0; +} + +/** + * \brief + * Start a HNP request + * + * \param transceiver this transceiver + * + * \return error code + */ +static int otg1200_start_hnp (struct otg_transceiver *transceiver) +{ + struct otg *otg = the_controller; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -ENODEV; + } + if (transceiver->default_a + && (!transceiver->host || !transceiver->host->b_hnp_enable) +#ifdef CONFIG_USB_OTG_HIGHSPEED + && (!transceiver->companion || !transceiver->companion->b_hnp_enable) +#endif + ) + return -ENOTCONN; + if (!transceiver->default_a && + (!transceiver->gadget || !transceiver->gadget->b_hnp_enable)) + return -ENOTCONN; + + switch (transceiver->state & OTG_STATE_MASK) { + case OTG_STATE_B_HOST: + case OTG_STATE_A_HOST: + case OTG_STATE_A_PERIPHERAL: + break; + default: + ERR ("USB OTG: wrong OTG state for role swap\n"); + return -EILSEQ; + } + return 0; +} + +/** + * \brief + * Start a SRP request + * + * \param transceiver this transceiver + * + * \return error code + */ +static int otg1200_start_srp (struct otg_transceiver *transceiver) +{ + struct otg *otg = the_controller; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -EINVAL; + } + if ((transceiver->state & OTG_STATE_MASK) != OTG_STATE_B_IDLE) { + RES_FLAG (otg, OTG_B_aSSN_REQ); + ERR ("USB OTG: wrong OTG state for SRP\n"); + return -EBUSY; + } + SET_FLAG (otg, OTG_B_aSSN_REQ); + otg_req_state_chg (otg, OTG_B_aSSN_REQ); + return 0; +} + +/** + * \brief + * Start a USB host enumeration + * + * \param transceiver this transceiver + * + * \return error code + */ +int otg_hnp_start_enum (struct otg_transceiver *transceiver) +{ + struct otg *otg = the_controller; + struct usb_bus *bus; + int status = 0; + + if (unlikely (transceiver != otg_to_transceiver (otg))) { + ERR ("USB OTG: unknown transceiver\n"); + return -EINVAL; + } + if (otg->transceiver.state == OTG_STATE_B_HOST) { + + bus = otg->transceiver.host; + +#ifdef CONFIG_USB_OTG_HIGHSPEED + + if (!bus || !bus->is_b_host) { + bus = otg->transceiver.companion; + } +#endif + if (bus && bus->is_b_host) { + DBG ("USB OTG: in HNP, start enumeration ...\n"); + status = usb_bus_start_enum (bus, bus->otg_port); + } + else { + DBG ("USB OTG: no device to enumerate\n"); + } + } + else { + DBG ("USB OTG: in HNP: wrong state %02x\n", + otg->transceiver.state); + } + return status; +} + +/** + * \brief + * OTG application request + * + * \param params + * + * \return OTG_APP_REQ_ACK + */ +static int otg_app_request (u32 params) +{ + struct otg *otg = the_controller; + u32 flags = params; + struct usb_bus *bus; + + if (OTG_UDC_RWK_REQ & flags) { + + if (otg_gadget_extension.notify) { + otg_gadget_extension.notify (OTG_GADGET_REQ_WAKE); + } + flags &= ~((u32) OTG_UDC_RWK_REQ); + } + + if (OTG_AB_HNP_REQ & flags) { + + /* request HNP */ + + bus = otg->transceiver.host; + if (!bus->b_hnp_enable) { + +#ifdef CONFIG_USB_OTG_HIGHSPEED + bus = otg->transceiver.companion; + } + if (!bus->b_hnp_enable) { +#endif + INFO ("HNP not enabled, ignoring request\n"); + } + else { + if (!otg1200_start_hnp (&otg->transceiver) && + bus->root_hub->children [bus->otg_port - 1]) { + usb_suspend_device (bus->root_hub->children [bus->otg_port - 1], + PM_SUSPEND_MEM); + } + else { + ERR ("no device found for HNP\n"); + } + } + flags &= ~((u32) OTG_AB_HNP_REQ); + } + + if (OTG_A_CLR_ERR & flags) { + + /* request to clear error flags */ + + flags |= OTG_A_VBUS_FAILED; + } + + /* create mask of flags to clear */ + + flags &= (OTG_B_SRP_ERROR | OTG_A_VBUS_FAILED); + + if (flags) { + + /* reset error flags if requested */ + + RES_FLAG (otg, (flags)); + } + + /* prepare for pending requests (no more actions on OTG_A_bHNP_EN) */ + + flags = params & SW_REQUEST_MASK; + SET_FLAG (otg, (flags)); + + if (OTG_A_BUS_DROP & flags) { + + /* request to drop A-VBus: reset "rise" request if pending */ + + flags &= ~OTG_A_BUS_REQ; + RES_FLAG (otg, OTG_A_BUS_REQ); + } + if (OTG_A_BUS_REQ & flags) { + + /* request to rise A-VBus: reset "drop" request if pending */ + + RES_FLAG (otg, OTG_A_BUS_DROP); + } + if (OTG_B_BUS_DIS & flags) { + + /* request to disable B-dev: reset "ena" request if pending */ + + flags &= ~OTG_B_BUS_REQ; + RES_FLAG (otg, OTG_B_BUS_REQ); + } + if (OTG_B_BUS_REQ & flags) { + + /* request to enable B-dev: reset "disa" request if pending */ + + RES_FLAG (otg, OTG_B_BUS_DIS); + } + if (flags) { + + /* request still pending may force a state change */ + + otg_req_state_chg (otg, flags); + } + return OTG_APP_REQ_ACK; +} +#endif /* CONFIG_USB_OTG */ + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + +/** + * \brief + * OTG application query + * + * \param index select status info data + * + * \return status + */ +static u32 otg_app_query (int index) +{ + struct otg *otg = the_controller; + u32 temp = 0; + + if (index == 0) { + temp = otg->params | + readl (&otg->regs->sts); + +/*** HS-A0 WA: BUG-3943: gadget phy suspend issue ***/ +#ifdef UDC_HSA0_WORKAROUNDS + if (((readl (&otg->regs->ctl) & OTG_CTL_MUX_MASK) == + OTG_CTL_ENABLE_UDC) && + ((temp & OTG_STS_PSUS) || (~temp & OTG_STS_VBUSVLD))) { + temp |= OTG_FLAGS_UDC_SUSP; + } +#endif + } + else if (index == 1) { + temp = otg->transceiver.state; + } +#ifdef OTG_HNP_ERR_LOG + if (hnp_err) { + temp |= OTG_FLAGS_HNP_ERR; + } +#endif + return temp; +} + +/** + * \brief + * OTG ISR calling the main state machine + * + * \param irq IRQ number + * \param _dev + * \param r + * + * \return IRQ_HANDLED (system code) + */ +static irqreturn_t otg_isr (int irq, void *dev, struct pt_regs *r) +{ + struct otg *otg = (struct otg *) dev; + u32 interrupts, int_mask, temp; + + int_mask = readl (&otg->regs->inten); + if ((OTG_INT_GLOBAL & int_mask) && + (int_mask &= ~((u32) OTG_INT_GLOBAL)) && + (interrupts = int_mask & + (temp = readl (&otg->regs->intr)))) { + + writel (int_mask, &otg->regs->inten); + /* clear interrupt status */ + writel (temp, &otg->regs->intr); +/* +#ifdef CONFIG_USB_OTG + + if (GOT_EVENT (OTG_INT_PCC, interrupts) && + IS_BIT_SET (OTG_STS_PCON, readl (&otg->regs->sts))) { + otg_hnp_start_enum (&otg->transceiver); + } +#endif */ + /* filter out additional WA interrupts, they're done */ + /* don't want to see them in the state machine */ + + if (interrupts & ~state_mask) { + + /* events pending for the state machine */ + + otg_change_state (otg, (interrupts & ~state_mask), + &int_mask); + } + + /* enable interrupts and keep information about WA ints: */ + + state_mask = OTG_INT_ADDS & ~int_mask; + writel ((OTG_INT_ADDS | int_mask | OTG_INT_GLOBAL), + &otg->regs->inten); + } + return IRQ_HANDLED; +} +#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ + +/** + * \brief + * OTG probe: init hardware, register the driver + * + * \param otg otg controller info + * + * \return success + */ +static inline int __init otg_probe (struct otg *otg) +{ + int retval; +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + u32 temp; + int i; +#endif + + /* initialize the OTG controller */ + + VDBG ("OTG init ...\n"); + +#ifdef VERBOSE + /* print regs */ + print_regs (otg); +#endif + /* Make sure we'll remember the initial state */ + init_state = readl (&otg->regs->ctl); + VDBG (" OTG init state was %08x\n", init_state); + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* turn on the OTG controller */ + + writel ((init_state | OTG_CTL_PADEN), &otg->regs->ctl); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +#ifndef UDC_HSA0_WORKAROUNDS + + /* Enable OTG hardware (incl. auto pull-up enable) */ + + writel ((readl (&dev->regs->cap) | OTG_CAP_APU), &otg->regs->cap); +#endif + + /* initialize flags */ + otg->params = 0; +#endif + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* make sure all interrupts are disabled */ + + writel (OTG_INT_DISALL, &otg->regs->inten); + writel (OTG_INT_ENALL, &otg->regs->intr); + + /* Set multiplexer to neutral, get power control, drop VBus */ + + if (((init_state & OTG_CTL_MUX_MASK) == OTG_CTL_ENABLE_UHC) && + ((((init_state & OTG_CTL_PPO) && (init_state & OTG_CTL_PPWR))) || + ((~init_state & OTG_CTL_PPO) && + (readl (&otg->regs->sts) & OTG_STS_SESSVLD)))) { + + /* VBus still powered try to discharge VBus and set timer */ + + DBG ("Setting init state, trying to discharge VBus ...\n"); + + for (i = 0; i < 4; i++) { + writel (TIMER_PERIOD, &otg->regs->tmr); + writel ((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | + OTG_CTL_PPO | OTG_CTL_DISCHRG | + OTG_CTL_TMR_UNCOND), &otg->regs->ctl); + do { } + while (!(readl (&otg->regs->sts) & OTG_STS_TMH)); + } + writel ((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | OTG_CTL_PPO), + &otg->regs->ctl); + writel (OTG_INT_ENALL, &otg->regs->intr); +#ifdef DEBUG + if (readl (&otg->regs->sts) & OTG_STS_SESSVLD) { + + DBG (" VBus still high, external host connected\n"); + } + else { + + DBG (" VBus discharged\n"); + } +#endif + } + else { + DBG ("Setting init state\n"); + + writel ((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | OTG_CTL_PPO), + &otg->regs->ctl); + } +#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ + +#ifdef CONFIG_USB_OTGMUX_HOST + + /* Set multiplexer to host */ + writel ((readl (&otg->regs->cap) & ~((u32) OTG_CAP_APU)), + &otg->regs->cap); + +#ifdef CONFIG_SOC_AU1200 + + writel ((OTG_CTL_ENABLE_UHC | OTG_CTL_PPO | OTG_CTL_PPWR), + &otg->regs->ctl); + + /* Turn on VBus */ + AU1000GPIO_CLR_BIT (USB_VBUS_GPIO); +#else + writel (OTG_CTL_ENABLE_UHC, &otg->regs->ctl); +#endif + + INFO ("OTG functionality is disabled, port is assigned to host\n"); +#endif + VDBG ("OTG init done\n"); + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE + + /* registering to the test driver */ + usb_otg_test_register_driver (&otg_test_extension); +#endif +#ifndef CONFIG_TEST_MODE_ONLY +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* registering to the device driver */ + if (usb_gadget_register_otg (otg_get_transceiver)) { + ERR ("gadget driver registration failed\n"); + retval = -ENODEV; + goto err1; + } +#endif +#ifdef CONFIG_USB_OTG + + /* registering to the usbcore */ + if (usb_host_register_otg (otg_get_transceiver)) { + ERR ("usbcore registration failed\n"); + retval = -ENODEV; + goto err2; + } +#endif +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* finally activate OTG functionality */ + /* Enable timer interrupt, start timer, set state */ + + SET_OTG_TIMER (otg, IDSNS_WAIT); + CHANGE_STATE (otg, OTG_STATE_UNDEFINED, &temp); + CHECK_STATE (otg, OTG_STATE_UNDEFINED, &temp); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget needs disconnect notification ***/ + + /* clear all interrupts before enable */ + writel (readl (&otg->regs->intr), &otg->regs->intr); + + state_mask = ~temp & OTG_INT_ADDS; + writel ((OTG_INT_ADDS | temp | OTG_INT_GLOBAL), &otg->regs->inten); + + DBG ("OTG-HW initialized, now checking ID ...\n"); +#endif + + return 0; + +#ifndef CONFIG_TEST_MODE_ONLY +#ifdef CONFIG_USB_OTG + +err2: + +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + usb_gadget_unregister_otg (); +err1: + +#endif +#endif +#ifdef CONFIG_USB_OTG_TEST_INTERFACE + + usb_otg_test_unregister_driver (); +#endif + return retval; +} + +/** + * \brief + * OTG remove: deregister the driver, clean-up hardware + * + * \param otg otg controller info + * + * \return void + */ +static inline void __exit otg_remove (struct otg *otg) +{ + int muxer; + +#ifndef CONFIG_TEST_MODE_ONLY +#ifdef CONFIG_USB_OTG + + /* unregistering from the usb core */ + usb_host_deregister_otg (); +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* unregistering from the usb gadget */ + usb_gadget_unregister_otg (); + +#endif +#endif +#ifdef CONFIG_USB_OTG_TEST_INTERFACE + + /* unregistering from the test driver */ + usb_otg_test_unregister_driver (); +#endif + + /* clean up the OTG controller */ + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* Disable all interrupts */ + writel (OTG_INT_DISALL, &otg->regs->inten); + writel (OTG_INT_ENALL, &otg->regs->intr); + + /* reset state, terminate all connections */ + CHANGE_STATE (otg, OTG_STATE_UNDEFINED, &state_mask); + CHECK_STATE (otg, OTG_STATE_UNDEFINED, &state_mask); + otg->params = 0; +#endif + + muxer = init_state & (OTG_CTL_ENABLE_UHC | OTG_CTL_ENABLE_UDC); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +#ifdef UDC_HSA0_WORKAROUNDS + + /* Don't assign the port to the device controller */ + + if (!(muxer ^ OTG_CTL_ENABLE_UDC)) { + + init_state &= ~((u32) (OTG_CTL_MUX_MASK | OTG_CTL_PUEN)); + muxer = OTG_CTL_DISABLE_ALL; + } + VDBG ("OTG writing back corrected init state: %08x\n", init_state); +#else + /* Reset APU */ + + writel ((readl (&otg->regs->cap) & ~((u32) OTG_CAP_APU)), + &otg->regs->cap); + + VDBG ("OTG writing back init state: %08x\n", init_state); +#endif + /* Now, that's the moment to remember */ + /* Set dev muxer and pull up bits, turn off the OTG controller */ + +#ifdef CONFIG_SOC_AU1200 + + /* Turn off VBus */ + AU1000GPIO_SET_BIT (USB_VBUS_GPIO); +#endif + writel (init_state, &otg->regs->ctl); + + if (!(muxer ^ OTG_CTL_ENABLE_UHC)) { + INFO ("disabling OTG-HW, port is assigned to host\n"); + } + else if (!(muxer ^ OTG_CTL_ENABLE_UDC)) { + INFO ("disabling OTG-HW, port is assigned to device\n"); + } + else { + INFO ("disabling OTG-HW, port is not assigned\n"); + } + + VDBG ("OTG exit: OTG-HW disabled\n"); + + if (!muxer) { + INFO ("OTG HW disabled, port is not assigned\n"); + } +} + +#ifdef CONFIG_SOC_AU1200 +#ifdef USE_AU1200_PCI_DUMMY + +/** + * \brief + * OTG dev probe: enable, init controller hardware + * + * \param pdev dummy pci device pointer + * + * \return success + */ +static int __init otg_pci_probe (struct pci_dev *pdev) +{ + struct otg *otg; + void *base; + int retval; + char buf [8] = {0,0,0,0,0,0,0,0}, *bufp; + + /* alloc, and start init */ + otg = (struct otg *) kmalloc (sizeof(struct otg), SLAB_KERNEL); + if (!otg) { + ERR ("couldn't allocate memory for OTG driver\n"); + retval = -ENOMEM; + goto err1; + } + DBG ( "kmalloc: OTG driver: %p\n", otg); + + /* hold global device pointer */ + the_controller = otg; + + memset (otg, 0, sizeof(struct otg)); + spin_lock_init (&otg->lock); + + /* + * Fill in the dummy pci_dev. Only those members actually + * dereferenced in this driver are initialized. + */ + memset(pdev, 0, sizeof(struct pci_dev)); + pdev->resource[0].name = "UOC Registers"; + pdev->resource[0].start = USB_UOC_BASE; + pdev->resource[0].end = USB_UOC_BASE + USB_UOC_LEN - 1; + pdev->resource[0].flags = 0; + pdev->irq = AU1200_USB_INT; + + otg->pdev = pdev; + + au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) | + (1 << USBMSRMCFG_GMEMEN)), (USB_MSR_BASE + USB_MSR_MCFG)); + au_readl (USB_MSR_BASE + USB_MSR_MCFG); + /* au_sync (); */ + udelay (1000); + if (AU1000GPIO_INIT_BIT (USB_VBUS_GPIO)) + WARN ("Couldn't initialize power switch GPIO\n"); + otg->enabled = 1; + + if (!request_mem_region (pdev->resource[0].start, + pdev->resource[0].end + 1 + - pdev->resource[0].start, + driver_name)) { + ERR ("controller already in use\n"); + retval = -EBUSY; + goto err3; + } + otg->region = 1; + + base = ioremap_nocache (pdev->resource[0].start, + pdev->resource[0].end + 1 + - pdev->resource[0].start); + if (!base) { + ERR ("couldn't map memory\n"); + retval = -EFAULT; + goto err4; + } + otg->regs = (struct otg_regs *) base; + bufp = buf; + + otg->chiprev = (u16) read_c0_prid() & 0xff; + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* OTG transceiver info */ + otg->transceiver.dev = &pdev->dev; + otg_init_transceiver (otg_to_transceiver (otg)); + + /* make sure all interrupts are disabled */ + writel (OTG_INT_DISALL, &otg->regs->inten); + writel (OTG_INT_ENALL, &otg->regs->intr); + readl (&otg->regs->inten); + + /* irq setup after old hardware is cleaned up */ + if (!pdev->irq) { + ERR ("No IRQ. Check system setup!\n"); + retval = -ENODEV; + goto err5; + } + snprintf (buf, sizeof buf, "%d", pdev->irq); + bufp = buf; + if (request_irq (pdev->irq, otg_isr, SA_INTERRUPT | SA_SHIRQ, + driver_name, otg) != 0) { + ERR ("request interrupt %s failed\n", bufp); + retval = -EBUSY; + goto err5; + } + otg->got_irq = 1; + + /* done */ + INFO ("%s\n", driver_desc); + INFO ("irq %s, mem %08lx, chip rev %02x (Au1200 %s)\n", + bufp, pdev->resource[0].start, otg->chiprev, + (otg->chiprev ? "AC" : "AB")); + +#else /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ + + INFO ("%s\n", driver_desc); + INFO ("mem %08lx, chip rev %02x (Au1200 %s)\n", + pdev->resource[0].start, otg->chiprev, + (otg->chiprev ? "AC" : "AB")); +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || defined(CONFIG_USB_OTGMUX_HOST) + + INFO ("using GPIO_%d for VBus control\n", USB_VBUS_GPIO); +#endif + bufp = DRIVER_VERSION; + INFO ("driver version: %s\n", bufp); + + if ((retval = otg_probe (otg)) == 0) { + pci_set_drvdata (pdev, otg); + return 0; + } + + /* something went wrong */ + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + otg->got_irq = 0; + free_irq (pdev->irq, otg); +err5: + +#endif + otg->regs = NULL; + iounmap (base); +err4: + otg->region = 0; + release_mem_region (pdev->resource[0].start, + pdev->resource[0].end + 1 + - pdev->resource[0].start); +err3: + otg->enabled = 0; + AU1000GPIO_TERM_BIT (USB_VBUS_GPIO); + au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) & + ~((u32) (1 << USBMSRMCFG_GMEMEN))), + (USB_MSR_BASE + USB_MSR_MCFG)); + au_readl (USB_MSR_BASE + USB_MSR_MCFG); + /* au_sync (); */ + udelay (1000); + + otg->pdev = NULL; + the_controller = NULL; + DBG ( "kfree: OTG driver: %p\n", otg); + kfree (otg); +err1: + otg = NULL; + return retval; +} + +/** + * \brief + * OTG dev remove: clean-up, disable controller hardware + * + * \param pdev dummy pci device info + * + * \return void + */ +static void __exit otg_pci_remove (struct pci_dev *pdev) +{ + struct otg *otg = pci_get_drvdata (pdev); + + otg_remove (otg); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + otg->got_irq = 0; + free_irq (pdev->irq, otg); +#endif + iounmap (otg->regs); + otg->regs = NULL; + otg->region = 0; + release_mem_region (pdev->resource[0].start, + pdev->resource[0].end + 1 + - pdev->resource[0].start); + otg->enabled = 0; + AU1000GPIO_TERM_BIT (USB_VBUS_GPIO); +/* au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) & + ~((u32) (1 << USBMSRMCFG_GMEMEN))), + (USB_MSR_BASE + USB_MSR_MCFG)); */ + au_readl (USB_MSR_BASE + USB_MSR_MCFG); + /* au_sync (); */ + udelay (1000); + otg->pdev = NULL; + the_controller = NULL; + DBG ( "kfree: OTG driver: %p\n", otg); + kfree (otg); + otg = NULL; + pci_set_drvdata(pdev, NULL); +} +#else + +/** + * \brief + * OTG dev probe: enable, init controller hardware + * + * \param dev platform device info + * + * \return success + */ +static int __init otg_drv_probe (struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct otg *otg; + u32 resource, len, irq; + void *base; + int retval; + char buf [8] = {0,0,0,0,0,0,0,0}, *bufp; + + /* alloc, and start init */ + otg = (struct otg *) kmalloc (sizeof(struct otg), SLAB_KERNEL); + if (!otg) { + ERR ("couldn't allocate memory for OTG driver\n"); + retval = -ENOMEM; + goto err1; + } + DBG ( "kmalloc: OTG driver: %p\n", otg); + + /* hold global device pointer */ + the_controller = otg; + + memset (otg, 0, sizeof(struct otg)); + spin_lock_init (&otg->lock); + + if (pdev->resource[0].flags != IORESOURCE_MEM) { + ERR ("resource is not IORESOURCE_MEM\n"); + retval = -ENOMEM; + goto err2; + } + resource = pdev->resource[0].start; + len = pdev->resource[0].end + 1 - pdev->resource[0].start; + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + ERR ("resource is not IORESOURCE_IRQ\n"); + retval = -ENOMEM; + goto err2; + } + irq = pdev->resource[1].start; + + otg->pdev = pdev; + + au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) | + (1 << USBMSRMCFG_GMEMEN)), (USB_MSR_BASE + USB_MSR_MCFG)); + au_readl (USB_MSR_BASE + USB_MSR_MCFG); + /* au_sync (); */ + udelay (1000); + if (AU1000GPIO_INIT_BIT (USB_VBUS_GPIO)) + WARN ("Couldn't initialize power switch GPIO\n"); + otg->enabled = 1; + + if (!request_mem_region (resource, len, driver_name)) { + ERR ("controller already in use\n"); + retval = -EBUSY; + goto err3; + } + otg->region = 1; + + base = ioremap_nocache (resource, len); + if (!base) { + ERR ("couldn't map memory\n"); + retval = -EFAULT; + goto err4; + } + otg->regs = (struct otg_regs *) base; + bufp = buf; + + otg->chiprev = (u16) read_c0_prid() & 0xff; + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* OTG transceiver info */ + otg->transceiver.dev = dev; + otg_init_transceiver (otg_to_transceiver (otg)); + + /* make sure all interrupts are disabled */ + writel (OTG_INT_DISALL, &otg->regs->inten); + writel (OTG_INT_ENALL, &otg->regs->intr); + readl (&otg->regs->inten); + + /* irq setup after old hardware is cleaned up */ + if (!irq) { + ERR ("No IRQ. Check system setup!\n"); + retval = -ENODEV; + goto err5; + } + snprintf (buf, sizeof buf, "%d", irq); + bufp = buf; + if (request_irq (irq, otg_isr, SA_INTERRUPT | SA_SHIRQ, + driver_name, otg) != 0) { + ERR ("request interrupt %s failed\n", bufp); + retval = -EBUSY; + goto err5; + } + otg->got_irq = 1; + + /* done */ + INFO ("%s\n", driver_desc); + INFO ("irq %s, mem %08x, chip rev %02x (Au1200 %s)\n", + bufp, resource, otg->chiprev, (otg->chiprev ? "AC" : "AB")); + +#else /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ + + INFO ("%s\n", driver_desc); + INFO ("mem %08x, chip rev %02x (Au1200 %s)\n", + resource, otg->chiprev, (otg->chiprev ? "AC" : "AB")); +#endif +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || defined(CONFIG_USB_OTGMUX_HOST) + + INFO ("using GPIO_%d for VBus control\n", USB_VBUS_GPIO); +#endif + bufp = DRIVER_VERSION; + INFO ("driver version: %s\n", bufp); + + if ((retval = otg_probe (otg)) == 0) { + dev_set_drvdata (dev, otg); + return 0; + } + + /* something went wrong */ + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + otg->got_irq = 0; + free_irq (irq, otg); +err5: + +#endif + otg->regs = NULL; + iounmap (base); +err4: + otg->region = 0; + release_mem_region (resource, len); +err3: + otg->enabled = 0; + AU1000GPIO_TERM_BIT (USB_VBUS_GPIO); + au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) & + ~((u32) (1 << USBMSRMCFG_GMEMEN))), + (USB_MSR_BASE + USB_MSR_MCFG)); + au_readl (USB_MSR_BASE + USB_MSR_MCFG); + /* au_sync (); */ + udelay (1000); +err2: + otg->pdev = NULL; + the_controller = NULL; + DBG ( "kfree: OTG driver: %p\n", otg); + kfree (otg); +err1: + otg = NULL; + + return retval; +} + +/** + * \brief + * OTG dev remove: clean-up, disable controller hardware + * + * \param dev platform device info + * + * \return void + */ +static int __exit otg_drv_remove (struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct otg *otg = dev_get_drvdata (dev); + + otg_remove (otg); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + otg->got_irq = 0; + free_irq (pdev->resource[1].start, otg); +#endif + iounmap (otg->regs); + otg->regs = NULL; + otg->region = 0; + release_mem_region (pdev->resource[0].start, + pdev->resource[0].end + 1 + - pdev->resource[0].start); + otg->enabled = 0; + AU1000GPIO_TERM_BIT (USB_VBUS_GPIO); +/* au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) & + ~((u32) (1 << USBMSRMCFG_GMEMEN))), + (USB_MSR_BASE + USB_MSR_MCFG)); */ + au_readl (USB_MSR_BASE + USB_MSR_MCFG); + /* au_sync (); */ + udelay (1000); + otg->pdev = NULL; + the_controller = NULL; + DBG ( "kfree: OTG driver: %p\n", otg); + kfree (otg); + otg = NULL; + dev_set_drvdata(dev, NULL); + return 0; +} +#endif +#else +#ifdef CONFIG_PCI + +/** + * \brief + * OTG PCI probe: enable, init controller hardware + * + * \param pdev pci device info + * \param id pci ids + * + * \return success + */ +static int __init otg_pci_probe (struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct otg *otg; + u32 resource, len; + void *base; + int retval; + char buf [8] = {0,0,0,0,0,0,0,0}, *bufp; + + /* alloc, and start init */ + otg = (struct otg *) kmalloc (sizeof(struct otg), SLAB_KERNEL); + if (!otg) { + ERR ("couldn't allocate memory for OTG driver\n"); + retval = -ENOMEM; + goto err1; + } + DBG ( "kmalloc: OTG driver: %p\n", otg); + + /* hold global device pointer */ + the_controller = otg; + + memset (otg, 0, sizeof(struct otg)); + spin_lock_init (&otg->lock); + + otg->pdev = pdev; + + /* now all the pci goodies ... */ + if (pci_enable_device (pdev) < 0) { + ERR ("couldn't enable PCI device\n"); + retval = -ENODEV; + goto err2; + } + otg->enabled = 1; + + resource = pci_resource_start (pdev, 0); + len = pci_resource_len (pdev, 0); + + if (!request_mem_region (resource, len, driver_name)) { + ERR ("controller already in use\n"); + retval = -EBUSY; + goto err3; + } + otg->region = 1; + + base = ioremap_nocache (resource, len); + if (!base) { + ERR ("couldn't map memory\n"); + retval = -EFAULT; + goto err4; + } + otg->regs = (struct otg_regs *) base; + bufp = buf; + + pci_read_config_byte (pdev, PCI_REVISION_ID, (u8*) &otg->chiprev); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + /* OTG transceiver info */ + + otg->transceiver.dev = &pdev->dev; + otg_init_transceiver (otg_to_transceiver (otg)); + + /* make sure all interrupts are disabled */ + writel (OTG_INT_DISALL, &otg->regs->inten); + writel (OTG_INT_ENALL, &otg->regs->intr); + readl (&otg->regs->inten); + + /* irq setup after old hardware is cleaned up */ + if (!pdev->irq) { + ERR ("No IRQ. Check PCI setup!\n"); + retval = -ENODEV; + goto err5; + } +#ifndef __sparc__ + snprintf (buf, sizeof buf, "%d", pdev->irq); + bufp = buf; +#else + bufp = __irq_itoa(pdev->irq); +#endif + if (request_irq (pdev->irq, otg_isr, SA_INTERRUPT | SA_SHIRQ, + driver_name, otg) != 0) { + ERR ("request interrupt %s failed\n", bufp); + retval = -EBUSY; + goto err5; + } + otg->got_irq = 1; + + /* done */ + INFO ("%s\n", driver_desc); + INFO ("irq %s, pci mem %08x, chip rev %02x (Geode5536 %s)\n", + bufp, resource, otg->chiprev, (otg->chiprev - 1 ? "B1" : "A0")); + +#else /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ + + INFO ("%s\n", driver_desc); + INFO ("pci mem %08x, chip rev %02x (Geode5536 %s)\n", + resource, otg->chiprev, (otg->chiprev - 1 ? "B1" : "A0")); +#endif + bufp = DRIVER_VERSION; + INFO ("driver version: %s\n", bufp); + + if ((retval = otg_probe (otg)) == 0) { + pci_set_drvdata (pdev, otg); + return 0; + } + + /* something went wrong */ + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + otg->got_irq = 0; + free_irq (pdev->irq, otg); +err5: + +#endif + otg->regs = NULL; + iounmap (base); +err4: + otg->region = 0; + release_mem_region (resource, len); +err3: + otg->enabled = 0; + pci_disable_device (pdev); +err2: + otg->pdev = NULL; + the_controller = NULL; + DBG ( "kfree: OTG driver: %p\n", otg); + kfree (otg); +err1: + otg = NULL; + return retval; +} + +/** + * \brief + * OTG PCI remove: clean-up, disable controller hardware + * + * \param pdev pci device info + * + * \return void + */ +static void __exit otg_pci_remove (struct pci_dev *pdev) +{ + struct otg *otg = pci_get_drvdata (pdev); + + otg_remove (otg); + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/*** HS-A0 WA: BUG-3950: gadget disconnect notification ***/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + + otg->got_irq = 0; + free_irq (pdev->irq, otg); +#endif + iounmap (otg->regs); + otg->regs = NULL; + otg->region = 0; + release_mem_region (pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + otg->enabled = 0; + pci_disable_device (pdev); + otg->pdev = NULL; + the_controller = NULL; + DBG ( "kfree: OTG driver: %p\n", otg); + kfree (otg); + otg = NULL; + pci_set_drvdata(pdev, NULL); +} +#endif +#endif + + +/***************************************************************************** + * More data + *****************************************************************************/ + +#ifdef CONFIG_SOC_AU1200 +#ifndef USE_AU1200_PCI_DUMMY +/** + * \brief + * driver struct to be used for driver registration + * + */ +static struct device_driver otg_device_driver = { + .name = "au1xxx-uoc", + .bus = &platform_bus_type, + .probe = otg_drv_probe, + .remove = otg_drv_remove, +/* .suspend = otg_drv_suspend, */ +/* .resume = otg_drv_resume, */ +}; +#endif +#else +#ifdef CONFIG_PCI +/** + * \brief + * PCI ID table + * + */ +static struct pci_device_id pci_ids [] = { { + .vendor = PCI_VENDOR_ID_AMD, + .device = PCI_DEVICE_ID_AMD_CS5536_UOC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = 0, + .class_mask = 0, + +}, { /* end: all zeroes */ } +}; + +/** + * \brief + * PCI driver struct to be used for driver registration + * ( this is a "new style" PCI driver module ) + * + */ +static struct pci_driver otg_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + .probe = otg_pci_probe, + .remove = otg_pci_remove, +/* .suspend = otg_pci_suspend, */ +/* .resume = otg_pci_resume, */ +}; +MODULE_DEVICE_TABLE (pci, pci_ids); +#endif +#endif + +/* This comment closes the module definition from above. There can be multiple + definitions of this kind in a file. See the doxygen documentation for more + information. */ +/** \}*/ + +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_AUTHOR ("Karsten Boge"); +MODULE_LICENSE ("GPL"); + +#ifdef CONFIG_SOC_AU1200 +#ifdef USE_AU1200_PCI_DUMMY + +static int __init init (void) +{ + return otg_pci_probe (&pdev_dummy); +} +static void __exit cleanup (void) +{ + otg_pci_remove (&pdev_dummy); +} +#else + +static int __init init (void) +{ + return driver_register (&otg_device_driver); +} +static void __exit cleanup (void) +{ + driver_unregister (&otg_device_driver); +} +#endif +#else +#ifdef CONFIG_PCI + +static int __init init (void) +{ + return pci_module_init (&otg_pci_driver); +} +static void __exit cleanup (void) +{ + pci_unregister_driver (&otg_pci_driver); +} +#endif +#endif + +module_init (init); +module_exit (cleanup); diff --git a/drivers/usb/gadget/au1200uoc.h b/drivers/usb/gadget/au1200uoc.h new file mode 100644 index 0000000..9b4503e --- /dev/null +++ b/drivers/usb/gadget/au1200uoc.h @@ -0,0 +1,1338 @@ +/* + * AMD Au1200 USB OTG controller driver + */ + +/* + * Copyright (C) 2005 AMD (http://www.amd.com) + * Author: Karsten Boge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + */ +#ifndef AU1200UOC_H +#define AU1200UOC_H + + +/***************************************************************************** +* Config options +*****************************************************************************/ + +#ifndef UDC_HSA0_WORKAROUNDS +#define UDC_HSA0_WORKAROUNDS +#endif + +/* #define USE_AU1200_PCI_DUMMY */ +#undef USE_AU1200_PCI_DUMMY + +#ifdef CONFIG_USB_OTG +#undef CONFIG_USB_OTGMUX_HOST +#undef CONFIG_USB_OTGMUX_GADGET +#undef CONFIG_USB_OTGMUX_IDPIN +#endif + +#ifdef CONFIG_USB_OTGMUX_IDPIN +#undef CONFIG_USB_OTGMUX_HOST +#ifndef CONFIG_USB_OTGMUX_GADGET +#define CONFIG_USB_OTGMUX_GADGET +#endif +#endif + +#ifndef OTG_HSA0_HNP_WA +#define OTG_HSA0_HNP_WA +#endif + +#ifdef VERBOSE +#ifndef DEBUG +#define DEBUG +#endif +#endif + + +/***************************************************************************** +* Constants +*****************************************************************************/ + +#define OTG_DRIVER_NAME "au1200otg" + +#define OTG_CHIPREV 0 + +#define OTG_FLAGS_ACTIV (1<<19) /* full OTG functionality is activ */ + +#ifndef CONFIG_SOC_AU1200 +#ifdef CONFIG_PCI +#ifndef PCI_VENDOR_ID_AMD +#define PCI_VENDOR_ID_AMD 0x1022 +#endif +#ifndef PCI_DEVICE_ID_AMD_CS5536_UOC +#define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097 +#endif +#else +#error "!!! UNKNOWN SYSTEM BUS TYPE !!!" +#endif +#endif + +#ifdef CONFIG_SOC_AU1200 +#define GPIO_2_BASE 200 +#define GPIO_215 215 +#define SYS_PINFUNC_P0A_GPIO (2<<17) +#ifdef CONFIG_MIPS_PB1200 +#define USB_VBUS_GPIO 3 /* AU1000_GPIO_3 */ +#endif +#ifdef CONFIG_MIPS_DB1200 +#define USB_VBUS_GPIO GPIO_215 +#endif +#if !(defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200)) +#error "!!! UNKNOWN AU1200 BOARD !!!" +#endif +#endif + + +/********************************** +* UOC Register definitions +**********************************/ + +/* capabilities */ +#define OTG_CAP_APU (1<<15) /* automatic pull-up enable */ + +/* multiplexer */ +#define OTG_MUX_DISABLE_ALL 0 /* not assigned */ +#define OTG_MUX_ENABLE_UHC (2<<0) /* assigned to host */ +#define OTG_MUX_ENABLE_UDC (3<<0) /* assigned to device */ +#define OTG_MUX_PUEN (1<<2) /* pull-up enable */ +#define OTG_MUX_VBUSVLD (1<<8) /* VBus valid */ + +/* status */ +#define OTG_STS_ID (1<<0) /* ID pin status */ +#define OTG_STS_VBUSVLD (1<<1) /* VBus valid */ +#define OTG_STS_SESSVLD (1<<2) /* Session valid */ +#define OTG_STS_SESSEND (1<<3) /* Session end */ +#define OTG_STS_LST (3<<4) /* Line state */ +#define OTG_STS_LST_J (1<<4) /* Line state */ +#define OTG_STS_LST_K (2<<4) /* Line state */ +#define OTG_STS_PSPD (3<<6) /* Port speed */ +#define OTG_STS_PSPD_LS (2<<6) /* Port speed */ +#define OTG_STS_PSPD_FS (1<<6) /* Port speed */ +#define OTG_STS_FSOE (1<<8) /* FS output enable (OHC) */ +#define OTG_STS_PCON (1<<9) /* Port connected */ +#define OTG_STS_PSUS (1<<10) /* Port suspended */ +#define OTG_STS_TMH (1<<11) /* Timer halted */ +#define OTG_STS_HNP_EN (1<<12) /* HNP enabled for B-dev */ +#define OTG_STS_HNP_SUPP (1<<13) /* A-host supports HNP */ +#define OTG_STS_HNP_ALTSUPP (1<<14) /* A-host supports alt. HNP */ +#define OTG_STS_HNPSTS (OTG_STS_HNP_EN | OTG_STS_HNP_SUPP | \ + OTG_STS_HNP_ALTSUPP) +#define OTG_STS_OC (1<<15) /* over-current */ +#define OTG_STS_DPR (1<<16) /* Downstream port reset */ + +/* control */ +#define OTG_CTL_DISABLE_ALL 0 /* not assigned */ +#define OTG_CTL_ENABLE_UHC (2<<0) /* assigned to host */ +#define OTG_CTL_ENABLE_UDC (3<<0) /* assigned to device */ +#define OTG_CTL_MUX_MASK (3<<0) /* port mux mask */ +#define OTG_CTL_PPWR (1<<2) /* port power switch */ +#define OTG_CTL_PPO (1<<3) /* port power override */ +#define OTG_CTL_CHRG (1<<4) /* charge VBus */ +#define OTG_CTL_DISCHRG (1<<5) /* discharge VBus */ +#define OTG_CTL_IDSNSEN (1<<6) /* ID sense enable, ID-PU */ +#define OTG_CTL_PADEN (1<<7) +#define OTG_CTL_PUEN (1<<8) /* pull-up enable */ +#define OTG_CTL_DMPDEN (1<<9) /* pull-down enable */ +#define OTG_CTL_HNPSFEN (1<<10) /* HNP SET_FEATURE enable */ +#define OTG_CTL_WPCS_DEAS (2<<16) /* deassert port connect */ +#define OTG_CTL_WPCS_ASRT (3<<16) /* assert port connect */ +#define OTG_CTL_WPSS_DEAS (2<<18) /* deassert port suspend */ +#define OTG_CTL_WPSS_ASRT (3<<18) /* assert port suspend */ +/* timer conditions */ +#define OTG_CTL_TMR_RLP (1<<28) /* timer reload policy */ +#define OTG_CTL_TMR_ALL (0xf<<24) /* stop timer */ +#define OTG_CTL_TMR_STOP 0 /* timer disabled */ +#define OTG_CTL_TMR_UNCOND (1<<24) /* count unconditionally */ +#define OTG_CTL_TMR_SE0 (2<<24) /* count if LSt = FS-SE0 */ +#define OTG_CTL_TMR_FSJ (3<<24) /* count if LSt = FS-J */ +#define OTG_CTL_TMR_FSK (4<<24) /* count if LSt = FS-K */ +#define OTG_CTL_TMR_NOSE0 (5<<24) /* count if LSt <> FS-SE0 */ +#define OTG_CTL_TMR_NORX (6<<24) /* count if Rx inactiv */ +#define OTG_CTL_TMR_ID (7<<24) /* count if ID = 0 */ + +/* interrupts */ +#define OTG_INT_GLOBAL (1<<31) /* global interrupt enable */ +#define OTG_INT_ENALL 0x7fff /* enable all */ +#define OTG_INT_DISALL 0 /* disable all */ +#define OTG_INT_IDC (1<<0) /* ID pin change */ +#define OTG_INT_VBVC (1<<1) /* VBUS valid change */ +#define OTG_INT_SVC (1<<2) /* Session valid change */ +#define OTG_INT_SEC (1<<3) /* Session end change */ +#define OTG_INT_LSTC (1<<4) /* Line state change */ +#define OTG_INT_PSPDC (1<<5) /* Port speed change */ +#define OTG_INT_FSOEC (1<<6) /* FS/LS OE change */ +#define OTG_INT_HSDD (1<<7) /* HS disconnect detected */ +#define OTG_INT_RXACT (1<<8) /* Rx activity detected */ +#define OTG_INT_PCC (1<<9) /* Port connect change */ +#define OTG_INT_PSC (1<<10) /* Port suspend change */ +#define OTG_INT_TMX (1<<11) /* Timer expired */ +#define OTG_INT_HNPFC (1<<12) /* HNP feature change */ +#define OTG_INT_OCD (1<<13) /* over current detected */ +#define OTG_INT_DPRC (1<<14) /* Downstream port reset change */ + +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3950: gadget SV notification ***/ +#ifdef UDC_HSA0_WORKAROUNDS +#define OTG_INT_ADDS OTG_INT_SVC +#endif + + +/********************************** +* OTG state dependend data +**********************************/ + +/* + * generic + */ +#define OTG_CTL_DEFAULT (OTG_CTL_PADEN | \ + OTG_CTL_IDSNSEN) +#define OTG_CTL_HOST_DEFAULT (OTG_CTL_DEFAULT | \ + OTG_CTL_ENABLE_UHC) +#ifdef UDC_HSA0_WORKAROUNDS +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +#ifdef CONFIG_USB_OTG +#define OTG_CTL_PERIPHERAL_DEFAULT (OTG_CTL_DEFAULT | \ + OTG_CTL_HNPSFEN | \ + OTG_CTL_ENABLE_UDC | \ + OTG_CTL_PPO | OTG_CTL_PUEN) +#else +#define OTG_CTL_PERIPHERAL_DEFAULT (OTG_CTL_DEFAULT | \ + OTG_CTL_ENABLE_UDC | \ + OTG_CTL_PPO | OTG_CTL_PUEN) +#endif +#else +#ifdef CONFIG_USB_OTG +#define OTG_CTL_PERIPHERAL_DEFAULT (OTG_CTL_DEFAULT | \ + OTG_CTL_HNPSFEN | \ + OTG_CTL_ENABLE_UDC | OTG_CTL_PPO) +#else +#define OTG_CTL_PERIPHERAL_DEFAULT (OTG_CTL_DEFAULT | \ + OTG_CTL_ENABLE_UDC | OTG_CTL_PPO) +#endif +#endif + +#define OTG_INT_DEFAULT OTG_INT_IDC + +/* + * OTG_STATE_UNDEFINED + */ +#define OTG_STATE_UNDEFINED_CONTROL (OTG_CTL_DEFAULT | OTG_CTL_PPO | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_UNDEFINED_STATUS 0 +#define OTG_STATE_UNDEFINED_STATUS_MASK 0 +#define OTG_STATE_UNDEFINED_INTERRUPTS OTG_INT_TMX + +/* + * OTG_STATE_A_IDLE + */ +#define OTG_STATE_A_IDLE_CONTROL (OTG_CTL_DEFAULT | OTG_CTL_PPO) +#define OTG_STATE_A_IDLE_STATUS 0 +#define OTG_STATE_A_IDLE_STATUS_MASK 0 +#define OTG_STATE_A_IDLE_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_LSTC) + +/* + * OTG_STATE_A_IDLE_WAIT_DP + */ +#define OTG_STATE_A_IDLE_WAIT_DP_CONTROL (OTG_STATE_A_IDLE_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_IDLE_WAIT_DP_STATUS 0 +#define OTG_STATE_A_IDLE_WAIT_DP_STATUS_MASK 0 +#define OTG_STATE_A_IDLE_WAIT_DP_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \ + OTG_INT_TMX) + +/* + * OTG_STATE_A_IDLE_WAIT_VP + */ +#define OTG_STATE_A_IDLE_WAIT_VP_CONTROL (OTG_STATE_A_IDLE_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_IDLE_WAIT_VP_STATUS 0 +#define OTG_STATE_A_IDLE_WAIT_VP_STATUS_MASK 0 +#define OTG_STATE_A_IDLE_WAIT_VP_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \ + OTG_INT_TMX) + +/* + * OTG_STATE_A_IDLE_WAIT_MP + */ +#define OTG_STATE_A_IDLE_WAIT_MP_CONTROL (OTG_STATE_A_IDLE_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_IDLE_WAIT_MP_STATUS 0 +#define OTG_STATE_A_IDLE_WAIT_MP_STATUS_MASK 0 +#define OTG_STATE_A_IDLE_WAIT_MP_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \ + OTG_INT_TMX) + +/* + * OTG_STATE_A_IDLE_WAIT_DV + */ +#define OTG_STATE_A_IDLE_WAIT_DV_CONTROL (OTG_STATE_A_IDLE_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_IDLE_WAIT_DV_STATUS 0 +#define OTG_STATE_A_IDLE_WAIT_DV_STATUS_MASK 0 +#define OTG_STATE_A_IDLE_WAIT_DV_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \ + OTG_INT_TMX) + +/* + * OTG_STATE_A_WAIT_VRISE + */ +#define OTG_STATE_A_WAIT_VRISE_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_WAIT_VRISE_STATUS 0 +#define OTG_STATE_A_WAIT_VRISE_STATUS_MASK 0 +#define OTG_STATE_A_WAIT_VRISE_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \ + OTG_INT_VBVC) + +/* + * OTG_STATE_A_WAIT_BCON + */ +#define OTG_STATE_A_WAIT_BCON_CONTROL OTG_CTL_HOST_DEFAULT +#define OTG_STATE_A_WAIT_BCON_STATUS 0 +#define OTG_STATE_A_WAIT_BCON_STATUS_MASK 0 +#define OTG_STATE_A_WAIT_BCON_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \ + OTG_INT_VBVC | OTG_INT_PCC) + +/* + * OTG_STATE_A_WAIT_BCON_VB + */ +#define OTG_STATE_A_WAIT_BCON_VB_CONTROL (OTG_STATE_A_WAIT_BCON_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_WAIT_BCON_VB_STATUS 0 +#define OTG_STATE_A_WAIT_BCON_VB_STATUS_MASK 0 +#define OTG_STATE_A_WAIT_BCON_VB_INTERRUPTS OTG_STATE_A_WAIT_BCON_INTERRUPTS + +/* + * OTG_STATE_A_HOST + */ +#define OTG_STATE_A_HOST_CONTROL OTG_CTL_HOST_DEFAULT +#define OTG_STATE_A_HOST_STATUS 0 +#define OTG_STATE_A_HOST_STATUS_MASK 0 +#ifdef CONFIG_USB_OTG +#ifndef VERBOSE +#define OTG_STATE_A_HOST_INTERRUPTS (OTG_INT_DEFAULT | \ + OTG_INT_VBVC | OTG_INT_DPRC | \ + OTG_INT_PCC | OTG_INT_PSC) +#else +#define OTG_STATE_A_HOST_INTERRUPTS (OTG_INT_DEFAULT | \ + OTG_INT_VBVC | OTG_INT_DPRC | \ + OTG_INT_PCC | OTG_INT_PSC | \ + OTG_INT_PSPDC) + /* OTG_INT_LSTC */ +#endif +#else + /* IDPIN mode only */ +#define OTG_STATE_A_HOST_INTERRUPTS OTG_INT_IDC +#endif + +/* + * OTG_STATE_A_SUSPEND + */ +#define OTG_STATE_A_SUSPEND_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_SUSPEND_STATUS 0 +#define OTG_STATE_A_SUSPEND_STATUS_MASK 0 +#define OTG_STATE_A_SUSPEND_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \ + OTG_INT_VBVC | OTG_INT_DPRC | \ + OTG_INT_PCC | OTG_INT_PSC) + +/* + * OTG_STATE_A_PERIPHERAL + */ +#define OTG_STATE_A_PERIPHERAL_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT | \ + OTG_CTL_PPWR | OTG_CTL_DMPDEN) +#define OTG_STATE_A_PERIPHERAL_STATUS 0 +#define OTG_STATE_A_PERIPHERAL_STATUS_MASK 0 +#ifndef VERBOSE +#define OTG_STATE_A_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | \ + OTG_INT_VBVC | OTG_INT_OCD | \ + OTG_INT_PCC | OTG_INT_PSC) +#else +#define OTG_STATE_A_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | \ + OTG_INT_VBVC | OTG_INT_OCD | \ + OTG_INT_PCC | OTG_INT_PSC | \ + OTG_INT_PSPDC) + /* OTG_INT_LSTC */ +#endif + +/* + * OTG_STATE_A_VBUS_ERR + */ +#define OTG_STATE_A_VBUS_ERR_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_PPO | OTG_CTL_DISCHRG) +#define OTG_STATE_A_VBUS_ERR_STATUS 0 +#define OTG_STATE_A_VBUS_ERR_STATUS_MASK 0 +#define OTG_STATE_A_VBUS_ERR_INTERRUPTS OTG_INT_DEFAULT + +/* + * OTG_STATE_A_WAIT_VFALL + */ +#define OTG_STATE_A_WAIT_VFALL_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_PPO) +#define OTG_STATE_A_WAIT_VFALL_STATUS 0 +#define OTG_STATE_A_WAIT_VFALL_STATUS_MASK 0 +#define OTG_STATE_A_WAIT_VFALL_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SEC) + +/* + * OTG_STATE_A_WAIT_VFALL_DN + */ +#define OTG_STATE_A_WAIT_VFALL_DN_CONTROL (OTG_STATE_A_WAIT_VFALL_CONTROL | \ + OTG_CTL_DISCHRG) +#define OTG_STATE_A_WAIT_VFALL_DN_STATUS 0 +#define OTG_STATE_A_WAIT_VFALL_DN_STATUS_MASK 0 +#define OTG_STATE_A_WAIT_VFALL_DN_INTERRUPTS OTG_STATE_A_WAIT_VFALL_INTERRUPTS + +/* + * OTG_STATE_A_WAIT_BDISCON + */ +#define OTG_STATE_A_WAIT_BDISCON_CONTROL (OTG_CTL_DEFAULT | \ + OTG_CTL_PPO | OTG_CTL_PPWR | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_A_WAIT_BDISCON_STATUS 0 +#define OTG_STATE_A_WAIT_BDISCON_STATUS_MASK 0 +#define OTG_STATE_A_WAIT_BDISCON_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \ + OTG_INT_VBVC | OTG_INT_OCD | \ + OTG_INT_PSPDC | OTG_INT_LSTC) + +/* + * OTG_STATE_B_IDLE + */ +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +#ifdef UDC_HSA0_WORKAROUNDS +#define OTG_STATE_B_IDLE_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT & \ + ~((u32) (OTG_CTL_PUEN | \ + OTG_CTL_ENABLE_UDC))) +#else +#define OTG_STATE_B_IDLE_CONTROL OTG_CTL_PERIPHERAL_DEFAULT +#endif +#define OTG_STATE_B_IDLE_STATUS 0 +#define OTG_STATE_B_IDLE_STATUS_MASK 0 +#ifdef CONFIG_USB_OTG +#define OTG_STATE_B_IDLE_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC) +#else +#ifdef CONFIG_USB_OTGMUX_IDPIN + /* IDPIN mode */ +#define OTG_STATE_B_IDLE_INTERRUPTS (OTG_INT_IDC | OTG_INT_SVC) +#else + /* gadget mode */ +#define OTG_STATE_B_IDLE_INTERRUPTS OTG_INT_SVC +#endif +#endif + +/* + * OTG_STATE_B_PERIPHERAL + */ +#define OTG_STATE_B_PERIPHERAL_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT | \ + OTG_CTL_DMPDEN) +#define OTG_STATE_B_PERIPHERAL_STATUS 0 +#define OTG_STATE_B_PERIPHERAL_STATUS_MASK 0 +#ifdef CONFIG_USB_OTG +#ifndef VERBOSE +#define OTG_STATE_B_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC | OTG_INT_PSC | \ + OTG_INT_HNPFC) +#else +#define OTG_STATE_B_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC | OTG_INT_PSC | \ + OTG_INT_HNPFC | OTG_INT_PSPDC) + /* OTG_INT_LSTC */ +#endif +#else +#ifdef CONFIG_USB_OTGMUX_IDPIN + /* IDPIN mode */ +#define OTG_STATE_B_PERIPHERAL_INTERRUPTS (OTG_INT_IDC | OTG_INT_SVC) +#else + /* gadget mode */ +#define OTG_STATE_B_PERIPHERAL_INTERRUPTS OTG_INT_SVC +#endif +#endif + +/* + * OTG_STATE_B_PERIPHERAL_WT + */ +#define OTG_STATE_B_PERIPHERAL_WT_CONTROL (OTG_STATE_B_PERIPHERAL_CONTROL | \ + OTG_CTL_PPO | OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_PERIPHERAL_WT_STATUS 0 +#define OTG_STATE_B_PERIPHERAL_WT_STATUS_MASK 0 +#define OTG_STATE_B_PERIPHERAL_WT_INTERRUPTS (OTG_STATE_B_PERIPHERAL_INTERRUPTS \ + | OTG_INT_TMX) + +/* + * OTG_STATE_B_PERIPHERAL_DC + */ +#define OTG_STATE_B_PERIPHERAL_DC_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_PPO | OTG_CTL_DMPDEN | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_PERIPHERAL_DC_STATUS 0 +#define OTG_STATE_B_PERIPHERAL_DC_STATUS_MASK 0 +#define OTG_STATE_B_PERIPHERAL_DC_INTERRUPTS (OTG_STATE_B_PERIPHERAL_INTERRUPTS \ + | OTG_INT_TMX | OTG_INT_LSTC) + +/* + * OTG_STATE_B_WAIT_ACON + */ +#define OTG_STATE_B_WAIT_ACON_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_PPO | OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_WAIT_ACON_STATUS 0 +#define OTG_STATE_B_WAIT_ACON_STATUS_MASK 0 +#define OTG_STATE_B_WAIT_ACON_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC | OTG_INT_PSC | \ + OTG_INT_HNPFC | OTG_INT_TMX) + +/* + * OTG_STATE_B_HOST + */ +#define OTG_STATE_B_HOST_CONTROL (OTG_CTL_HOST_DEFAULT | \ + OTG_CTL_PPO) +#define OTG_STATE_B_HOST_STATUS 0 +#define OTG_STATE_B_HOST_STATUS_MASK 0 +#ifndef VERBOSE +#define OTG_STATE_B_HOST_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC) +#else +#define OTG_STATE_B_HOST_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC | OTG_INT_SVC | \ + OTG_INT_PSPDC) + /* OTG_INT_LSTC */ +#endif + +/* + * OTG_STATE_B_HOST_WT + */ +#define OTG_STATE_B_HOST_WT_CONTROL (OTG_STATE_B_HOST_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_HOST_WT_STATUS 0 +#define OTG_STATE_B_HOST_WT_STATUS_MASK 0 +#ifndef VERBOSE +#define OTG_STATE_B_HOST_WT_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC | OTG_INT_TMX) +#else +#define OTG_STATE_B_HOST_WT_INTERRUPTS (OTG_STATE_B_HOST_INTERRUPTS | \ + OTG_INT_TMX) +/* +#define OTG_STATE_B_HOST_WT_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \ + OTG_INT_PCC | OTG_INT_DPRC | \ + OTG_INT_TMX | OTG_INT_PSPDC) */ + /* OTG_INT_LSTC */ +#endif + +/* + * OTG_STATE_B_SRP_INIT + */ +#define OTG_STATE_B_SRP_INIT_CONTROL OTG_STATE_B_IDLE_CONTROL +#define OTG_STATE_B_SRP_INIT_STATUS 0 +#define OTG_STATE_B_SRP_INIT_STATUS_MASK 0 +#define OTG_STATE_B_SRP_INIT_INTERRUPTS OTG_INT_DEFAULT + +/* + * OTG_STATE_B_SRP_WTSE0 + */ +#define OTG_STATE_B_SRP_WAIT_SE0_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_SRP_WAIT_SE0_STATUS 0 +#define OTG_STATE_B_SRP_WAIT_SE0_STATUS_MASK 0 +#define OTG_STATE_B_SRP_WAIT_SE0_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \ + | OTG_INT_TMX | OTG_INT_LSTC) + +/* + * OTG_STATE_B_SRP_D_PLS + * + * note: changing to this state requires an additional call: + * set_srp_conditions (dev); + * reset_srp_conditions (dev) is required for the next state + */ +/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/ +/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/ +/* note: set_/reset_srp_conditions is not needed if HS-A0 WAs are active + */ +#ifdef UDC_HSA0_WORKAROUNDS +#define OTG_STATE_B_SRP_D_PULSE_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT | \ + OTG_CTL_PUEN | \ + OTG_CTL_TMR_UNCOND) +#else +#define OTG_STATE_B_SRP_D_PULSE_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \ + OTG_CTL_PUEN | \ + OTG_CTL_TMR_UNCOND) +#endif +#define OTG_STATE_B_SRP_D_PULSE_STATUS 0 +#define OTG_STATE_B_SRP_D_PULSE_STATUS_MASK 0 +#define OTG_STATE_B_SRP_D_PULSE_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \ + | OTG_INT_SEC | OTG_INT_TMX) + +/* + * OTG_STATE_B_SRP_V_PLS + */ +#define OTG_STATE_B_SRP_V_PULSE_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \ + OTG_CTL_CHRG | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_SRP_V_PULSE_STATUS 0 +#define OTG_STATE_B_SRP_V_PULSE_STATUS_MASK 0 +#define OTG_STATE_B_SRP_V_PULSE_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \ + | OTG_INT_TMX) + +/* + * OTG_STATE_B_SRP_V_DCG + */ +#define OTG_STATE_B_SRP_V_DCHRG_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \ + OTG_CTL_DISCHRG | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_SRP_V_DCHRG_STATUS 0 +#define OTG_STATE_B_SRP_V_DCHRG_STATUS_MASK 0 +#define OTG_STATE_B_SRP_V_DCHRG_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \ + | OTG_INT_TMX) + +/* + * OTG_STATE_B_SRP_WTVB + */ +#define OTG_STATE_B_SRP_WAIT_VBUS_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \ + OTG_CTL_TMR_UNCOND) +#define OTG_STATE_B_SRP_WAIT_VBUS_STATUS 0 +#define OTG_STATE_B_SRP_WAIT_VBUS_STATUS_MASK 0 +#define OTG_STATE_B_SRP_WAIT_VBUS_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \ + | OTG_INT_SVC | OTG_INT_TMX) + +/*********************************/ + +/* other */ + +#define OTG_APP_REQ_ACK 0 + + +/***************************************************************************** +* Types +*****************************************************************************/ + + +/***************************************************************************** +* Macros +*****************************************************************************/ + +/* printing messages */ +#ifndef INFO +#define INFO(args...) \ + printk(KERN_INFO DRIVER_NAME_FOR_PRINT ": " args) +#endif + +#ifndef WARN +#define WARN(args...) \ + printk(KERN_WARNING DRIVER_NAME_FOR_PRINT " warning: " args) +#endif + +#ifndef ERR +#define ERR(args...) \ + printk(KERN_ERR DRIVER_NAME_FOR_PRINT " error: " args) +#endif + +#ifndef DBG +#ifdef DEBUG +#define DBG(args...) \ + printk(KERN_DEBUG DRIVER_NAME_FOR_PRINT " debug: " args) +#else +#define DBG(args...) \ + do {} while (0) +#endif +#endif + +#ifndef VDBG +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(args...) \ + do { } while (0) +#endif +#endif + +/****************************************************************************/ + +/* this should always return "1" and print something in verbose mode */ +#ifdef VERBOSE +#define VDBG_SPC(fmt,args...) \ + (VDBG (fmt, args) ? 1 : 1) +#else +#define VDBG_SPC(fmt,args...) 1 +#endif + +/* query bit(s) (long: 32-bit access) */ +#define IS_BIT_RES(data, code) \ + (!((data) & (code)) ? \ + (VDBG_SPC (" OTG HW status: %s is reset\n", #data)) : 0) + +#define IS_BIT_SET(data, code) \ + (((data) & (code)) ? \ + (VDBG_SPC (" OTG HW status: %s is set\n", #data)) : 0) + +/* query SW flag(s) */ +#define IS_FLAG_RES(dev, data) \ + (!((data) & (dev)->params) ? \ + (VDBG_SPC (" OTG SW status: %s is reset\n", #data)) : 0) + +#define IS_FLAG_SET(dev, data) \ + (((data) & (dev)->params) ? \ + (VDBG_SPC (" OTG SW status: %s is set\n", #data)) : 0) + +/* query event bit(s) */ +#define GOT_EVENT(data, code) \ + (((data) & (code)) ? \ + (VDBG_SPC (" OTG event: %s\n", #data)) : 0) + +/* set SW flag */ +#ifdef VERBOSE +#define SET_FLAG(dev, data) \ + if (!((data) & (dev)->params)) \ + DBG (" OTG SW status change: set flag %s\n", #data); \ + (dev)->params |= (data) +#else +#define SET_FLAG(dev, data) \ + (dev)->params |= (data) +#endif + +/* reset SW flag */ +#ifdef VERBOSE +#define RES_FLAG(dev, data) \ + if ((data) & (dev)->params) \ + DBG (" OTG SW status change: reset flag %s\n", #data); \ + (dev)->params &= ~((u32) (data)) +#else +#define RES_FLAG(dev, data) \ + (dev)->params &= ~((u32) (data)) +#endif + +/* reset event bit */ +#define RES_EVENT(data, code) \ + (code) &= ~((u32) (data)) +/* NOTE: this is not really needed so far, might be replaced with */ +/* #define RES_EVENT(data, code) \ */ +/* do {} while (0) */ + +/* change OTG state */ +#ifdef CONFIG_USB_OTG +#define PREPARE_STATE_CHANGE_1ST(dev, new_state) \ + switch ((new_state) & OTG_STATE_MASK) { \ + case OTG_STATE_UNDEFINED: \ + set_undef_state_defaults((dev)); \ + break; \ + case OTG_STATE_A_IDLE: \ + set_a_state_defaults((dev)); \ + break; \ + case OTG_STATE_B_IDLE: \ + set_b_state_defaults((dev)); \ + break; \ + default: \ + break; \ + } \ + do {} while (0) +#else +#define PREPARE_STATE_CHANGE_1ST(dev, new_state) \ + do {} while (0) +#endif + +#ifdef CONFIG_SOC_AU1200 + +#if (USB_VBUS_GPIO < GPIO_2_BASE) +#define AU1000GPIO_SET_BIT(data) \ + au1000gpio_bit_set (data) +#define AU1000GPIO_CLR_BIT(data) \ + au1000gpio_bit_clear (data) +#define AU1000GPIO_INIT_BIT(data) \ + au1000gpio_bit_init (data) +#define AU1000GPIO_TERM_BIT(data) \ + au1000gpio_bit_term (data) +#else +#define AU1000GPIO_SET_BIT(data) \ + au1000gpio2_bit_set (data) +#define AU1000GPIO_CLR_BIT(data) \ + au1000gpio2_bit_clear (data) +#define AU1000GPIO_INIT_BIT(data) \ + au1000gpio2_bit_init (data) +#define AU1000GPIO_TERM_BIT(data) \ + au1000gpio2_bit_term (data) +#endif + +#define PREPARE_STATE_CHANGE_2ND(new_state_control) \ + if (((new_state_control) & OTG_CTL_PPO) && \ + (~(new_state_control) & OTG_CTL_PPWR)) \ + AU1000GPIO_SET_BIT(USB_VBUS_GPIO); \ + else \ + AU1000GPIO_CLR_BIT(USB_VBUS_GPIO) +#else +#define PREPARE_STATE_CHANGE_2ND(new_state_control) \ + do {} while (0) +#endif + +#define CHANGE_STATE(dev, new_state, pMask) \ + PREPARE_STATE_CHANGE_1ST(dev, new_state); \ + PREPARE_STATE_CHANGE_2ND(new_state##_CONTROL); \ + writel ((new_state##_CONTROL), &(dev)->regs->ctl); \ + *(pMask) = (new_state##_INTERRUPTS); \ + (dev)->transceiver.state = (new_state); \ + DBG ("OTG new state: %s\n", #new_state) + +/* verify OTG state */ +#ifndef CONFIG_OTG_TEST_MODE + +#define CHECK_STATE(dev, act_state, pMask) \ + *(pMask) = (act_state##_INTERRUPTS); \ + (dev)->prv_state = (act_state); \ + VDBG ("OTG state: %s\n", #act_state) +#else +#define CHECK_STATE(dev, act_state, pMask) \ + *(pMask) = (act_state##_INTERRUPTS); \ + (dev)->prv_state = (act_state); \ + if (((readl (&(dev)->regs->sts) ^ (act_state##_STATUS))) & \ + act_state##_STATUS_MASK) \ + WARN ("OTG warning: incorrect status\n"); \ + VDBG ("OTG state: %s\n", #act_state) +#endif + +/* set timer */ +#define SET_OTG_TIMER(dev, val) \ + set_timer ((dev), ((OTG_TMR_##val) * 100)) + +/* set timer ( <1ms ) */ +#define SET_OTG_TIMER_SHORT(dev, val) \ + set_timer ((dev), ((OTG_TMR_##val) / 10)) + +/* set timer ( >10ms ) */ +#define SET_OTG_TIMER_LONG(dev, val) \ + set_timer_long ((dev), ((OTG_TMR_##val) / 10)) + +#ifdef VERBOSE +#define HS_DISCON_WARNING() \ + if (!(OTG_CTL_ENABLE_UHC ^ \ + (OTG_CTL_MUX_MASK & readl (&dev->regs->ctl))) && \ + !(OTG_STS_PSPD & readl (&dev->regs->sts))) \ + DBG (" OTG warning: disable UHC from HS-mode\n") +#else +#define HS_DISCON_WARNING() \ + do { } while (0) +#endif + + +/***************************************************************************** +* Data +*****************************************************************************/ +struct otg_regs { + u32 cap; /* capabilities */ + u32 mux; /* mux */ + u32 sts; /* status */ + u32 ctl; /* control */ + u32 tmr; /* timer */ + u32 intr; /* interrupt request */ + u32 inten; /* interrupt enable */ +} __attribute__ ((packed)); + +struct otg { + spinlock_t lock; + unsigned enabled : 1, + got_irq : 1, + region : 1; + u16 chiprev; + + /* state variables */ + u8 prv_state; + u32 params; + void *otg_priv; + u8 hostcount; + +#ifdef CONFIG_SOC_AU1200 +#ifdef USE_AU1200_PCI_DUMMY + + struct pci_dev *pdev; +#else + struct platform_device *pdev; +#endif +#else + struct pci_dev *pdev; +#endif + struct otg_regs *regs; + struct otg_transceiver transceiver; +}; +#define transceiver_to_otg(pTransceiver) \ + container_of (pTransceiver, struct otg, transceiver) +#define otg_to_transceiver(pOtg) \ + &pOtg->transceiver + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE +struct usb_otg_test_extension { + int (*bind) (struct otg **); + int (*unbind) (struct otg **); + int (*request) (u32); /* function call for state change requests */ + u32 (*query) (int); /* function call to query state */ +}; +#endif + +#ifdef __KERNEL__ + +/* 2.5 and 2.4.older portability changes ... */ + +#ifndef container_of +#define container_of list_entry +#endif + +#ifndef likely +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifndef BUG_ON +#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) +#endif + +#ifndef WARN_ON +#define WARN_ON(x) do { } while (0) +#endif + +#endif /* __KERNEL__ */ + +/***************************************************************************** +* Functions +*****************************************************************************/ +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) +extern int usb_gadget_register_otg (struct otg_transceiver * ( + *get_transceiver)(void)); +extern int usb_gadget_unregister_otg (void); + +#endif +#ifdef CONFIG_USB_OTG +u32 otg_change_state (struct otg *, u32, u32 *); +int otg_hnp_start_enum (struct otg_transceiver *); +#endif + +void otg_init_state (struct otg *); +int otg_exit_state (struct otg *); + +#ifdef DEBUG +static void print_regs (struct otg *); +#endif /* DEBUG */ + +#ifdef CONFIG_USB_OTG_TEST_INTERFACE +int usb_otg_test_register_driver (struct usb_otg_test_extension *); +int usb_otg_test_unregister_driver (void); +#endif + +/***************************************************************************** +* Inline Functions +*****************************************************************************/ + +extern u32 otg_tmr_high_count; +extern struct otg_ctl *otg_ctl; + +#ifdef CONFIG_USB_OTG +/** + * \brief + * set neutral state information + * + * \param dev OTG controller info + * + * \return void + */ +static inline void set_undef_state_defaults (struct otg *dev) +{ + dev->transceiver.default_a = 0; + if (dev->transceiver.host) { + dev->transceiver.host->is_b_host = 0; + } +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (dev->transceiver.companion) { + dev->transceiver.companion->is_b_host = 0; + } +#endif + if (dev->transceiver.gadget) { + dev->transceiver.gadget->is_a_peripheral = 0; + } +} + +/** + * \brief + * set A state information + * + * \param dev OTG controller info + * + * \return void + */ +static inline void set_a_state_defaults (struct otg *dev) +{ + dev->transceiver.default_a = 1; + if (dev->transceiver.host) { + dev->transceiver.host->is_b_host = 0; + } +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (dev->transceiver.companion) { + dev->transceiver.companion->is_b_host = 0; + } +#endif + if (dev->transceiver.gadget) { + dev->transceiver.gadget->is_a_peripheral = 1; + } +} + +/** + * \brief + * set B state information + * + * \param dev OTG controller info + * + * \return void + */ +static inline void set_b_state_defaults (struct otg *dev) +{ + dev->transceiver.default_a = 0; + if (dev->transceiver.host) { + dev->transceiver.host->is_b_host = 1; + } +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (dev->transceiver.companion) { + dev->transceiver.companion->is_b_host = 1; + } +#endif + if (dev->transceiver.gadget) { + dev->transceiver.gadget->is_a_peripheral = 0; + } +} + +/** + * \brief + * set B state information + * + * \param dev OTG controller info + * + * \return void + */ +static inline void reset_b_hnp_enable (struct otg *dev) +{ + if (dev->transceiver.host) { + dev->transceiver.host->b_hnp_enable = 0; + } +#ifdef CONFIG_USB_OTG_HIGHSPEED + if (dev->transceiver.companion) { + dev->transceiver.companion->b_hnp_enable = 0; + } +#endif + VDBG (" OTG action: HNP disabled in B-device\n"); +} + +/** + * \brief + * set B state information + * + * \param dev OTG controller info + * + * \return void + */ +static inline int is_b_hnp_enabled (struct otg *dev) +{ + int retVal = 0; + + if (dev->transceiver.host && + dev->transceiver.host->b_hnp_enable) { + VDBG (" OTG status: HNP is enabled in HS-B-device\n"); + retVal = 1; + } +#ifdef CONFIG_USB_OTG_HIGHSPEED + else if (dev->transceiver.companion && + dev->transceiver.companion->b_hnp_enable) { + VDBG (" OTG status: HNP is enabled in FS-B-device\n"); + retVal = 1; + } +#endif +#ifdef VERBOSE + else { + DBG (" OTG status: HNP is disabled in B-device\n"); + } +#endif + return retVal; +} +#endif + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET) + +/** + * \brief + * Read the status register + * + * \param dev OTG controller info + * + * \return status + */ +static inline u32 get_status (struct otg *dev) +{ + return (readl (&dev->regs->sts)); +} + +/** + * \brief + * Load and start the timer for an unconditional run + * + * \param dev OTG controller info + * \param val Value to load + * + * \return void + */ +static inline void set_timer (struct otg *dev, u32 val) +{ + otg_tmr_high_count = 0; + + writel ((val), &dev->regs->tmr); + VDBG (" OTG action: start timer: %d0 us\n", val); +} + +/** + * \brief + * Load and start the timer for an unconditional run + * + * \param dev OTG controller info + * \param val Value to load + * + * \return void + */ +static inline void set_timer_long (struct otg *dev, u32 val) +{ + otg_tmr_high_count = val - 1; + + writel (TIMER_PERIOD, &dev->regs->tmr); + VDBG (" OTG action: start timer: %d0 ms\n", val); +} + +/** + * \brief + * Re-start the timer (value already loaded) + * + * \param dev OTG controller info + * + * \return void + */ +static inline void restart_timer (struct otg *dev) +{ + writel ((readl (&dev->regs->ctl) | OTG_CTL_TMR_UNCOND), + &dev->regs->ctl); +} + +/** + * \brief + * Reset the timer while running (value already loaded) + * + * \param dev OTG controller info + * + * \return void + */ +static inline void reset_timer (struct otg *dev) +{ + u32 temp; + + temp = readl (&dev->regs->ctl); + writel ((temp & ~((u32) OTG_CTL_TMR_ALL)), &dev->regs->ctl); + writel (temp, &dev->regs->ctl); + VDBG (" OTG action: re-start timer\n"); +} + +/** + * \brief + * Prepare the D-pulse + * + * \param dev OTG controller info + * + * \return void + */ +static inline void set_srp_conditions (struct otg *dev) +{ +#ifdef UDC_HSA0_WORKAROUNDS + VDBG (" OTG action: SRP init: no action needed due to A0 WAs\n"); +#else + writel ((readl (&dev->regs->cap) & ~((u32) OTG_CAP_APU)), + &dev->regs->cap); + VDBG (" OTG action: set SRP conditions: disable APU\n"); +#endif +} + +/** + * \brief + * Reset conditions after SRP + * + * activates the auto-pull-up feature so after SRP the host + * will detect a device connect after calling this function + * + * \param dev OTG controller info + * + * \return void + */ +static inline void reset_srp_conditions (struct otg *dev) +{ +#ifdef UDC_HSA0_WORKAROUNDS + VDBG (" OTG action: SRP done: no action needed due to A0 WAs\n"); +#else + writel ((readl (&dev->regs->cap) | OTG_CAP_APU), + &dev->regs->cap); + VDBG (" OTG action: reset SRP conditions: enable APU\n"); +#endif +} + +/** + * \brief + * enable HNP for both devices + * + * \param dev OTG controller info + * + * \return success + */ +static inline int otg_enable_hnp (struct otg *dev) +{ + int retVal = 0; + +/* if (!dev->device) { + ERR ("Tried to send OTG request, device pointer not valid\n"); + retVal = -ENODEV; + } + else { + retVal = usb_otg_enable_b_hnp (dev->device); + DBG (" OTG action: enabled HNP in B-device\n"); + } + if (retVal < 0) { + RES_FLAG (OTG_A_bHNP_EN); + } + else { + SET_FLAG (OTG_A_bHNP_EN); + }*/ + return retVal; +} + +#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */ +#ifdef DEBUG +/** + * \brief + * Print OTG controller registers (debug mode only) + * + * \param dev OTG controller info + * + * \return void + */ +static inline void print_regs (struct otg *dev) +{ + DBG( "-- UOC registers ---\n"); + DBG( "otg cap = %08x\n", readl (&dev->regs->cap)); + DBG( "otg mux = %08x\n", readl (&dev->regs->mux)); + DBG( "otg sts = %08x\n", readl (&dev->regs->sts)); + DBG( "otg ctl = %08x\n", readl (&dev->regs->ctl)); + DBG( "otg tmr = %08x\n", readl (&dev->regs->tmr)); + DBG( "otg intr = %08x\n", readl (&dev->regs->intr)); + DBG( "otg inten = %08x\n", readl (&dev->regs->inten)); + DBG( "--------------------\n"); +} +#endif /* DEBUG */ + +#ifdef CONFIG_SOC_AU1200 + +/** + * \brief + * GPIO functions (Au1200 only) + * + * \param data pin info + * + * \return success + */ +static inline int au1000gpio_bit_get (u32 data) +{ + return (au_readl ((SYS_PINSTATERD & (1 << data)) ? 1 : 0)); +} + +#ifdef CONFIG_AU1X00_GPIO + + /* GPIO functions are defined in drivers/char/au1000_gpio.c */ + +#else + +static inline int au1000gpio_bit_tristate (u32 data) +{ + au_writel ((1 << data), SYS_TRIOUTCLR); + return 0; +} + +static inline int au1000gpio_bit_set (u32 data) +{ + au_writel ((1 << data), SYS_OUTPUTSET); + return 0; +} + +static inline int au1000gpio_bit_clear (u32 data) +{ + au_writel ((1 << data), SYS_OUTPUTCLR); + return 0; +} + +static inline int au1000gpio_bit_init (u32 data) +{ + return au1000gpio_bit_set (data); +} + +static inline int au1000gpio_bit_term (u32 data) +{ + return au1000gpio_bit_set (data); +} + +static inline int au1000gpio2_bit_set (u32 data) +{ + au_writel (((1 << (data - GPIO_2_BASE)) | + (1 << (data - GPIO_2_BASE + 0x10))), GPIO2_OUTPUT); + au_sync(); + return 0; +} + +static inline int au1000gpio2_bit_clear (u32 data) +{ + au_writel ((1 << (data - GPIO_2_BASE + 0x10)), GPIO2_OUTPUT); + au_sync(); + return 0; +} + +static inline int au1000gpio2_bit_init (u32 data) +{ + u32 temp = au_readl (GPIO2_ENABLE); + + if (!(temp | 1)) { + au_writel (temp | 1, GPIO2_ENABLE); + au_readl (GPIO2_ENABLE); + udelay (20); + } + if (temp | 2) { + au_writel (((temp & 0xfffffffd) | 1), GPIO2_ENABLE); + au_readl (GPIO2_ENABLE); + } + + /* FIXME: check the system impact of the P0A setting */ + /* DbAu1200 pin (GPIO_215) pin is shared w/ PSC0 functions */ + /* setting this field to GPIO affects also GPIO_16, GPIO_31 */ + + if (data == GPIO_215) { + au_writel (((au_readl (SYS_PINFUNC) & + ~((u32) SYS_PINFUNC_P0A)) | SYS_PINFUNC_P0A_GPIO), + SYS_PINFUNC); + } + au_writel (au_readl (GPIO2_INTENABLE) & ~(1 << (data - GPIO_2_BASE)), + GPIO2_INTENABLE); + au_writel (au_readl (GPIO2_DIR) | (1 << (data - GPIO_2_BASE)), + GPIO2_DIR); + au_sync(); + return 0; +} + +static inline int au1000gpio2_bit_term (u32 data) +{ + return au1000gpio2_bit_set (data); +} +#endif +#endif /* CONFIG_SOC_AU1200 */ + +#endif /* AU1200UOC_H */ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 8f402f8..e147883 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -91,6 +91,13 @@ static const char driver_desc [] = DRIVE #define RX_EXTRA 20 /* guard against rx overflows */ +#ifdef CONFIG_USB_GADGET_AU1200UDC +/* + * AMD UDC supports dword aligned DMA buffers only, so skbuf buffers + * from network layer must be copied + */ +#define UDC_MAX_BUFLEN 2048 /* size of dword aligned buffer */ +#endif #include "rndis.h" #ifndef CONFIG_USB_ETH_RNDIS @@ -151,8 +158,13 @@ struct eth_dev { /* Thanks to NetChip Technologies for donating this product ID. * It's for devices with only CDC Ethernet configurations. */ +#ifdef CONFIG_USB_GADGET_AU1200UDC +#define CDC_VENDOR_NUM 0x0438 /* AMD */ +#define CDC_PRODUCT_NUM 0xdc01 /* AMD Linux-USB Ethernet Gadget (temporary) */ +#else #define CDC_VENDOR_NUM 0x0525 /* NetChip */ #define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ +#endif /* For hardware that can't talk CDC, we use the same vendor ID that * ARM Linux has used for ethernet-over-usb, both with sa1100 and @@ -253,6 +265,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethern #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_AU1200UDC +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -1056,6 +1072,10 @@ static void eth_reset_config (struct eth req = container_of (dev->tx_reqs.next, struct usb_request, list); list_del (&req->list); +#ifdef CONFIG_USB_GADGET_AU1200UDC + if (req->buf) + usb_ep_free_buffer (dev->in_ep, req->buf, req->dma, UDC_MAX_BUFLEN); +#endif usb_ep_free_request (dev->in_ep, req); } } @@ -1065,6 +1085,10 @@ static void eth_reset_config (struct eth req = container_of (dev->rx_reqs.next, struct usb_request, list); list_del (&req->list); +#ifdef CONFIG_USB_GADGET_AU1200UDC + if (req->buf) + usb_ep_free_buffer (dev->out_ep, req->buf, req->dma, UDC_MAX_BUFLEN); +#endif usb_ep_free_request (dev->out_ep, req); } } @@ -1632,7 +1656,9 @@ rx_submit (struct eth_dev *dev, struct u */ skb_reserve(skb, NET_IP_ALIGN); +#ifndef CONFIG_USB_GADGET_AU1200UDC req->buf = skb->data; +#endif req->length = size; req->complete = rx_complete; req->context = skb; @@ -1674,6 +1700,16 @@ static void rx_complete (struct usb_ep * break; } +#ifdef CONFIG_USB_GADGET_AU1200UDC + /* we need to copy here because AMD UDC needs dword aligned buffers */ + VDEBUG(dev, "memcpy( skb->data %lx,req->buf %lx,req->actual %d)\n", skb->data,req->buf,req->actual); + if (req->actual <= UDC_MAX_BUFLEN) { + memcpy(skb->data, req->buf, req->actual); + } + else { + ERROR(dev, "memcpy of %d bytes bigger than max %d\n", req->actual, UDC_MAX_BUFLEN); + } +#endif skb->dev = dev->net; skb->protocol = eth_type_trans (skb, dev->net); dev->stats.rx_packets++; @@ -1742,6 +1778,15 @@ static int prealloc (struct list_head *l req = usb_ep_alloc_request (ep, gfp_flags); if (!req) return list_empty (list) ? -ENOMEM : 0; + + +#ifdef CONFIG_USB_GADGET_AU1200UDC + /* allocate buffer which must be dword aligned for AMD UDC */ + req->buf = usb_ep_alloc_buffer (ep, UDC_MAX_BUFLEN, + &req->dma, GFP_ATOMIC); +#endif + + list_add (&req->list, list); } return 0; @@ -1753,6 +1798,10 @@ extra: next = req->list.next; list_del (&req->list); +#ifdef CONFIG_USB_GADGET_AU1200UDC + if (req->buf) + usb_ep_free_buffer (ep, req->buf, req->dma, UDC_MAX_BUFLEN); +#endif usb_ep_free_request (ep, req); if (next == list) @@ -1905,7 +1954,19 @@ static int eth_start_xmit (struct sk_buf rndis_add_hdr (skb); length = skb->len; } - req->buf = skb->data; +#ifdef CONFIG_USB_GADGET_AU1200UDC + /* we need to copy here because AMD UDC needs dword aligned buffers */ + VDEBUG(dev, "memcpy(req->buf %lx, skb->data %lx, length %d)", req->buf, skb->data, length); + if (length <= UDC_MAX_BUFLEN) { + memcpy(req->buf, skb->data, length); + } + else { + ERROR(dev, "memcpy of %d bytes bigger than max %d\n", length, UDC_MAX_BUFLEN); + } + VDEBUG(dev, "past memcpy(req->buf %lx, skb->data %lx, length %d)", req->buf, skb->data, length); +#else + req->buf = skb->data; +#endif req->context = skb; req->complete = tx_complete; @@ -2202,6 +2263,8 @@ eth_bind (struct usb_gadget *gadget) /* sh doesn't support multiple interfaces or configs */ cdc = 0; rndis = 0; + } else if (gadget_is_au1200udc(gadget)) { + rndis = 0; } else if (gadget_is_sa1100 (gadget)) { /* hardware can't write zlps */ zlp = 0; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index ea09aaa..ae800e6 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -263,8 +263,14 @@ MODULE_LICENSE("Dual BSD/GPL"); * * DO NOT REUSE THESE IDs with any other driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ + +#ifdef CONFIG_USB_GADGET_AU1200UDC +#define DRIVER_VENDOR_ID 0x0438 // AMD +#define DRIVER_PRODUCT_ID 0xdc02 // AMD Linux-USB File-backed Storage Gadget (temporary) +#else #define DRIVER_VENDOR_ID 0x0525 // NetChip #define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget +#endif /* @@ -3717,6 +3723,8 @@ static int __init check_parameters(struc mod_data.can_stall = 0; if (mod_data.release == 0xffff) { // Parameter wasn't set + if (gadget_is_au1200udc(fsg->gadget)) + mod_data.can_stall = 0; /* The sa1100 controller is not supported */ if (gadget_is_sa1100(fsg->gadget)) gcnum = -1; diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 8cbae21..541fbd0 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -15,6 +15,12 @@ #define gadget_is_net2280(g) 0 #endif +#ifdef CONFIG_USB_GADGET_AU1200UDC +#define gadget_is_au1200udc(g) !strcmp("au1200udc", (g)->name) +#else +#define gadget_is_au1200udc(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_DUMMY_HCD #define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) #else @@ -143,5 +149,7 @@ static inline int usb_gadget_controller_ return 0x13; else if (gadget_is_imx(gadget)) return 0x14; + else if (gadget_is_au1200udc(gadget)) + return 0x15; return -ENOENT; } diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 6c58636..4ec28bb 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -193,6 +193,10 @@ module_param (loopdefault, bool, S_IRUGO * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ +#ifdef CONFIG_USB_GADGET_AU1200UDC +#define DRIVER_VENDOR_NUM 0x0438 /* AMD */ +#define DRIVER_PRODUCT_NUM 0xdc00 /* AMD Linux-USB "Gadget Zero" (temporary) */ +#else #ifndef CONFIG_USB_ZERO_HNPTEST #define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ #define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */ @@ -200,6 +204,7 @@ module_param (loopdefault, bool, S_IRUGO #define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */ #define DRIVER_PRODUCT_NUM 0xbadd #endif +#endif /*-------------------------------------------------------------------------*/ @@ -1127,8 +1132,11 @@ zero_unbind (struct usb_gadget *gadget) DBG (dev, "unbind\n"); /* we've already been disconnected ... no i/o is active */ - if (dev->req) + if (dev->req) { + /* free with original length */ + dev->req->length = USB_BUFSIZ; free_ep_req (gadget->ep0, dev->req); + } del_timer_sync (&dev->resume); kfree (dev); set_gadget_data (gadget, NULL);