diff -Nru kernel/arch/arm/mach-msm/board-trout.c.orig kernel/arch/arm/mach-msm/board-trout.c --- kernel/arch/arm/mach-msm/board-trout.c.orig 2009-01-11 15:37:16.000000000 -0800 +++ kernel/arch/arm/mach-msm/board-trout.c 2009-01-20 15:35:33.000000000 -0800 @@ -26,7 +26,9 @@ #include #include #include +#ifdef CONFIG_USB_FUNCTION #include +#endif #include @@ -462,6 +464,7 @@ mdelay(10); } +#ifdef CONFIG_USB_FUNCTION static char *trout_usb_functions[] = { "usb_mass_storage", "adb", @@ -477,10 +480,12 @@ .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ }, }; +#endif static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_reset = trout_phy_reset, .phy_init_seq = trout_phy_init_seq, +#ifdef CONFIG_USB_FUNCTION .vendor_id = 0x0bb4, .product_id = 0x0c02, .version = 0x0100, @@ -491,6 +496,7 @@ .num_functions = ARRAY_SIZE(trout_usb_functions), .products = trout_usb_products, .num_products = ARRAY_SIZE(trout_usb_products), +#endif }; static struct platform_device msm_hsusb_device = { @@ -504,6 +510,7 @@ }, }; +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE static struct usb_mass_storage_platform_data mass_storage_pdata = { .nluns = 1, .buf_size = 16384, @@ -519,6 +526,7 @@ .platform_data = &mass_storage_pdata, }, }; +#endif static struct resource trout_ram_console_resource[] = { { @@ -680,7 +688,9 @@ &msm_serial2_device, #endif &msm_hsusb_device, +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE &usb_mass_storage_device, +#endif &trout_nav_device, &android_leds, &sd_door_switch, @@ -721,7 +731,9 @@ static int __init trout_serialno_setup(char *str) { +#ifdef CONFIG_USB_FUNCTION msm_hsusb_pdata.serial_number = str; +#endif return 1; } diff -Nru kernel/arch/arm/mach-msm/board-halibut.c.orig kernel/arch/arm/mach-msm/board-halibut.c --- kernel/arch/arm/mach-msm/board-halibut.c.orig 2009-01-11 15:37:16.000000000 -0800 +++ kernel/arch/arm/mach-msm/board-halibut.c 2009-01-20 15:29:08.000000000 -0800 @@ -171,19 +171,23 @@ */ static int halibut_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 }; +#ifdef CONFIG_USB_FUNCTION static char *halibut_usb_functions[] = { "diag", "adb", }; +#endif static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = halibut_phy_init_seq, +#ifdef CONFIG_USB_FUNCTION .vendor_id = 0x18d1, .product_id = 0xd00d, .version = 0x0100, .product_name = "Halibut", .functions = halibut_usb_functions, .num_functions = ARRAY_SIZE(halibut_usb_functions), +#endif }; static struct platform_device msm_hsusb_device = { diff -Nru kernel/scripts/setlocalversion.orig kernel/scripts/setlocalversion --- kernel/scripts/setlocalversion.orig 2009-01-11 15:38:12.000000000 -0800 +++ kernel/scripts/setlocalversion 2009-01-20 12:18:06.000000000 -0800 @@ -8,6 +8,10 @@ cd "${1:-.}" || usage +# jun's hack for fooling wlan.ko module +echo "-01843-gfea26b0" +exit 0 + # Check for git and a git repo. if head=`git rev-parse --verify HEAD 2>/dev/null`; then # Do we have an untagged version? diff -Nru kernel/include/asm-arm/arch-msm/msm_hsusb.h.orig kernel/include/asm-arm/arch-msm/msm_hsusb.h --- kernel/include/asm-arm/arch-msm/msm_hsusb.h.orig 2009-01-11 15:37:57.000000000 -0800 +++ kernel/include/asm-arm/arch-msm/msm_hsusb.h 2009-01-20 15:03:14.000000000 -0800 @@ -21,6 +21,7 @@ /* platform device data for msm_hsusb driver */ +#ifdef CONFIG_USB_FUNCTION /* matches a product ID to a list of enabled functions */ struct msm_hsusb_product { /* product ID for usb_device_descriptor.idProduct */ @@ -31,6 +32,7 @@ */ __u32 functions; }; +#endif struct msm_hsusb_platform_data { /* hard reset the ULPI PHY */ @@ -39,6 +41,7 @@ /* val, reg pairs terminated by -1 */ int *phy_init_seq; +#ifdef CONFIG_USB_FUNCTION /* USB device descriptor fields */ __u16 vendor_id; @@ -62,6 +65,7 @@ */ int num_products; struct msm_hsusb_product *products; +#endif }; #endif diff -Nru kernel/include/asm-arm/arch-msm/board.h.orig kernel/include/asm-arm/arch-msm/board.h --- kernel/include/asm-arm/arch-msm/board.h.orig 2009-01-11 15:37:57.000000000 -0800 +++ kernel/include/asm-arm/arch-msm/board.h 2009-01-20 15:00:55.000000000 -0800 @@ -77,7 +77,7 @@ struct mmc_platform_data; int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat); -#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) || defined(CONFIG_USB_MSM_72K) void msm_hsusb_set_vbus_state(int online); #else static inline void msm_hsusb_set_vbus_state(int online) {} diff -Nru kernel/drivers/usb/Kconfig.orig kernel/drivers/usb/Kconfig --- kernel/drivers/usb/Kconfig.orig 2009-01-11 15:37:50.000000000 -0800 +++ kernel/drivers/usb/Kconfig 2009-01-20 15:17:43.000000000 -0800 @@ -145,6 +145,6 @@ source "drivers/usb/gadget/Kconfig" -source "drivers/usb/function/Kconfig +source "drivers/usb/function/Kconfig" endif # USB_SUPPORT diff -Nru kernel/drivers/usb/gadget/ether.c.orig kernel/drivers/usb/gadget/ether.c --- kernel/drivers/usb/gadget/ether.c.orig 2009-01-20 17:01:36.000000000 -0800 +++ kernel/drivers/usb/gadget/ether.c 2009-01-20 17:01:54.000000000 -0800 @@ -207,6 +207,8 @@ /* Include CDC support if we could run on CDC-capable hardware. */ +#define DEV_CONFIG_CDC + #ifdef CONFIG_USB_GADGET_NET2280 #define DEV_CONFIG_CDC #endif diff -Nru kernel/drivers/usb/gadget/Kconfig.orig kernel/drivers/usb/gadget/Kconfig --- kernel/drivers/usb/gadget/Kconfig.orig 2009-01-11 15:37:50.000000000 -0800 +++ kernel/drivers/usb/gadget/Kconfig 2009-01-20 15:21:16.000000000 -0800 @@ -205,6 +205,7 @@ config USB_GADGET_M66592 boolean "Renesas M66592 USB Peripheral Controller" + depends on JSUN_HACK_TO_GET_RID_OF_THIS_SUCKER select USB_GADGET_DUALSPEED help M66592 is a discrete USB peripheral controller chip that @@ -335,6 +336,24 @@ depends on USB_GADGET_AT91 default USB_GADGET +config USB_GADGET_MSM_72K + boolean "MSM 72K Device Controller" + depends on ARCH_MSM7X00A + select USB_GADGET_SELECTED + select USB_GADGET_DUALSPEED + help + USB gadget driver for Qualcomm MSM 72K architecture. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "msm72k" and force all + gadget drivers to also be dynamically linked. + +config USB_MSM_72K + tristate + depends on USB_GADGET_MSM_72K + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL diff -Nru kernel/drivers/usb/gadget/gadget_chips.h.orig kernel/drivers/usb/gadget/gadget_chips.h --- kernel/drivers/usb/gadget/gadget_chips.h.orig 2009-01-11 15:37:50.000000000 -0800 +++ kernel/drivers/usb/gadget/gadget_chips.h 2009-01-20 15:00:55.000000000 -0800 @@ -147,6 +147,12 @@ #define gadget_is_m66592(g) 0 #endif +#ifdef CONFIG_USB_GADGET_MSM_72K +#define gadget_is_msm72k(g) !strcmp("msm72k_udc", (g)->name) +#else +#define gadget_is_msm72k(g) 0 +#endif + // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 @@ -212,5 +218,7 @@ return 0x20; else if (gadget_is_m66592(gadget)) return 0x21; + else if (gadget_is_msm72k(gadget)) + return 0x22; return -ENOENT; } diff -Nru kernel/drivers/usb/gadget/msm72k_udc.h.orig kernel/drivers/usb/gadget/msm72k_udc.h --- kernel/drivers/usb/gadget/msm72k_udc.h.orig 2009-01-20 15:00:55.000000000 -0800 +++ kernel/drivers/usb/gadget/msm72k_udc.h 2009-01-20 15:00:55.000000000 -0800 @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __LINUX_USB_GADGET_MSM72K_UDC_H__ +#define __LINUX_USB_GADGET_MSM72K_UDC_H__ + +/*-------------------------------------------------------------------------*/ + +#define xprintk(level, fmt, args...) \ + printk(level "%s: " fmt , driver_name , ## args) + +#ifdef DEBUG +#undef DEBUG +#define DEBUG(fmt, args...) \ + xprintk(KERN_DEBUG , fmt , ## args) +#else +#define DEBUG(fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDEBUG DEBUG +#else +#define VDEBUG(fmt,args...) \ + do { } while (0) +#endif /* VERBOSE */ + +#define ERROR(fmt,args...) \ + xprintk(KERN_ERR , fmt , ## args) +#define INFO(fmt,args...) \ + xprintk(KERN_INFO , fmt , ## args) + +/*-------------------------------------------------------------------------*/ + + +#define USB_ID (MSM_USB_BASE + 0x0000) +#define USB_HWGENERAL (MSM_USB_BASE + 0x0004) +#define USB_HWHOST (MSM_USB_BASE + 0x0008) +#define USB_HWDEVICE (MSM_USB_BASE + 0x000C) +#define USB_HWTXBUF (MSM_USB_BASE + 0x0010) +#define USB_HWRXBUF (MSM_USB_BASE + 0x0014) +#define USB_SBUSCFG (MSM_USB_BASE + 0x0090) + +#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ +#define USB_HCIVERSION (MSM_USB_BASE + 0x0102) /* 16 bit */ +#define USB_HCSPARAMS (MSM_USB_BASE + 0x0104) +#define USB_HCCPARAMS (MSM_USB_BASE + 0x0108) +#define USB_DCIVERSION (MSM_USB_BASE + 0x0120) /* 16 bit */ +#define USB_USBCMD (MSM_USB_BASE + 0x0140) +#define USB_USBSTS (MSM_USB_BASE + 0x0144) +#define USB_USBINTR (MSM_USB_BASE + 0x0148) +#define USB_FRINDEX (MSM_USB_BASE + 0x014C) +#define USB_DEVICEADDR (MSM_USB_BASE + 0x0154) +#define USB_ENDPOINTLISTADDR (MSM_USB_BASE + 0x0158) +#define USB_BURSTSIZE (MSM_USB_BASE + 0x0160) +#define USB_TXFILLTUNING (MSM_USB_BASE + 0x0164) +#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) +#define USB_ENDPTNAK (MSM_USB_BASE + 0x0178) +#define USB_ENDPTNAKEN (MSM_USB_BASE + 0x017C) +#define USB_PORTSC (MSM_USB_BASE + 0x0184) +#define USB_OTGSC (MSM_USB_BASE + 0x01A4) +#define USB_USBMODE (MSM_USB_BASE + 0x01A8) +#define USB_ENDPTSETUPSTAT (MSM_USB_BASE + 0x01AC) +#define USB_ENDPTPRIME (MSM_USB_BASE + 0x01B0) +#define USB_ENDPTFLUSH (MSM_USB_BASE + 0x01B4) +#define USB_ENDPTSTAT (MSM_USB_BASE + 0x01B8) +#define USB_ENDPTCOMPLETE (MSM_USB_BASE + 0x01BC) +#define USB_ENDPTCTRL(n) (MSM_USB_BASE + 0x01C0 + (4 * (n))) + + +#define USBCMD_RESET 2 +#define USBCMD_ATTACH 1 +#define USBCMD_ATDTW (1 << 14) + +#define USBMODE_DEVICE 2 +#define USBMODE_HOST 3 + +struct ept_queue_head { + unsigned config; + unsigned active; /* read-only */ + + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved_0; + + unsigned char setup_data[8]; + + unsigned reserved_1; + unsigned reserved_2; + unsigned reserved_3; + unsigned reserved_4; +}; + +#define CONFIG_MAX_PKT(n) ((n) << 16) +#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ +#define CONFIG_IOS (1 << 15) /* IRQ on setup */ + +struct ept_queue_item { + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved; +}; + +#define TERMINATE 1 + +#define INFO_BYTES(n) ((n) << 16) +#define INFO_IOC (1 << 15) +#define INFO_ACTIVE (1 << 7) +#define INFO_HALTED (1 << 6) +#define INFO_BUFFER_ERROR (1 << 5) +#define INFO_TXN_ERROR (1 << 3) + + +#define STS_NAKI (1 << 16) /* */ +#define STS_SLI (1 << 8) /* R/WC - suspend state entered */ +#define STS_SRI (1 << 7) /* R/WC - SOF recv'd */ +#define STS_URI (1 << 6) /* R/WC - RESET recv'd - write to clear */ +#define STS_FRI (1 << 3) /* R/WC - Frame List Rollover */ +#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */ +#define STS_UEI (1 << 1) /* R/WC - USB Error */ +#define STS_UI (1 << 0) /* R/WC - USB Transaction Complete */ + + +/* bits used in all the endpoint status registers */ +#define EPT_TX(n) (1 << ((n) + 16)) +#define EPT_RX(n) (1 << (n)) + + +#define CTRL_TXE (1 << 23) +#define CTRL_TXR (1 << 22) +#define CTRL_TXI (1 << 21) +#define CTRL_TXD (1 << 17) +#define CTRL_TXS (1 << 16) +#define CTRL_RXE (1 << 7) +#define CTRL_RXR (1 << 6) +#define CTRL_RXI (1 << 5) +#define CTRL_RXD (1 << 1) +#define CTRL_RXS (1 << 0) + +#define CTRL_TXT_MASK (3 << 18) +#define CTRL_TXT_CTRL (0 << 18) +#define CTRL_TXT_ISOCH (1 << 18) +#define CTRL_TXT_BULK (2 << 18) +#define CTRL_TXT_INT (3 << 18) + +#define CTRL_RXT_MASK (3 << 2) +#define CTRL_RXT_CTRL (0 << 2) +#define CTRL_RXT_ISOCH (1 << 2) +#define CTRL_RXT_BULK (2 << 2) +#define CTRL_RXT_INT (3 << 2) + +#define ULPI_WAKEUP (1 << 31) +#define ULPI_RUN (1 << 30) +#define ULPI_WRITE (1 << 29) +#define ULPI_READ (0 << 29) +#define ULPI_STATE_NORMAL (1 << 27) +#define ULPI_ADDR(n) (((n) & 255) << 16) +#define ULPI_DATA(n) ((n) & 255) +#define ULPI_DATA_READ(n) (((n) >> 8) & 255) + +/* USB_PORTSC bits for determining port speed */ +#define PORTSC_PSPD_FS (0 << 26) +#define PORTSC_PSPD_LS (1 << 26) +#define PORTSC_PSPD_HS (2 << 26) +#define PORTSC_PSPD_MASK (3 << 26) + +#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ diff -Nru kernel/drivers/usb/gadget/Makefile.orig kernel/drivers/usb/gadget/Makefile --- kernel/drivers/usb/gadget/Makefile.orig 2009-01-11 15:37:50.000000000 -0800 +++ kernel/drivers/usb/gadget/Makefile 2009-01-20 15:00:55.000000000 -0800 @@ -17,6 +17,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o # # USB gadget drivers diff -Nru kernel/drivers/usb/gadget/msm72k_udc.c.orig kernel/drivers/usb/gadget/msm72k_udc.c --- kernel/drivers/usb/gadget/msm72k_udc.c.orig 2009-01-20 15:00:55.000000000 -0800 +++ kernel/drivers/usb/gadget/msm72k_udc.c 2009-01-20 15:39:28.000000000 -0800 @@ -0,0 +1,1503 @@ +/* + * Driver for HighSpeed USB Client Controller in MSM7K + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +static const char driver_name[] = "msm72k_udc"; + +/* #define DEBUG */ +/* #define VERBOSE */ +#include "msm72k_udc.h" + +#define MSM_USB_BASE ((unsigned) ui->addr) + +#define DRIVER_DESC "MSM 72K USB Peripheral Controller" + +#define EPT_FLAG_IN 0x0001 + +#define SETUP_BUF_SIZE 4096 + + +static const char *const ep_name[] = { + "ep0out", "ep1out", "ep2out", "ep3out", + "ep4out", "ep5out", "ep6out", "ep7out", + "ep8out", "ep9out", "ep10out", "ep11out", + "ep12out", "ep13out", "ep14out", "ep15out", + "ep0in", "ep1in", "ep2in", "ep3in", + "ep4in", "ep5in", "ep6in", "ep7in", + "ep8in", "ep9in", "ep10in", "ep11in", + "ep12in", "ep13in", "ep14in", "ep15in" +}; + +/* current state of VBUS */ +static int vbus; + +struct msm_request { + struct usb_request req; + + /* saved copy of req.complete */ + void (*gadget_complete)(struct usb_ep *ep, + struct usb_request *req); + + + struct usb_info *ui; + struct msm_request *next; + + unsigned busy:1; + unsigned live:1; + unsigned alloced:1; + unsigned dead:1; + + dma_addr_t dma; + dma_addr_t item_dma; + + struct ept_queue_item *item; +}; + +#define to_msm_request(r) container_of(r, struct msm_request, req) +#define to_msm_endpoint(r) container_of(r, struct msm_endpoint, ep) + +struct msm_endpoint { + struct usb_ep ep; + struct usb_info *ui; + struct msm_request *req; /* head of pending requests */ + struct msm_request *last; + unsigned flags; + + /* bit number (0-31) in various status registers + ** as well as the index into the usb_info's array + ** of all endpoints + */ + unsigned char bit; + unsigned char num; + + /* pointers to DMA transfer list area */ + /* these are allocated from the usb_info dma space */ + struct ept_queue_head *head; +}; + +static void usb_do_work(struct work_struct *w); + + +#define USB_STATE_IDLE 0 +#define USB_STATE_ONLINE 1 +#define USB_STATE_OFFLINE 2 + +#define USB_FLAG_START 0x0001 +#define USB_FLAG_VBUS_ONLINE 0x0002 +#define USB_FLAG_VBUS_OFFLINE 0x0004 +#define USB_FLAG_RESET 0x0008 + +struct usb_info { + /* lock for register/queue/device state changes */ + spinlock_t lock; + + /* single request used for handling setup transactions */ + struct usb_request *setup_req; + + struct platform_device *pdev; + int irq; + void *addr; + + unsigned state; + unsigned flags; + + unsigned online:1; + unsigned running:1; + + struct dma_pool *pool; + + /* dma page to back the queue heads and items */ + unsigned char *buf; + dma_addr_t dma; + + struct ept_queue_head *head; + + /* used for allocation */ + unsigned next_item; + unsigned next_ifc_num; + + /* endpoints are ordered based on their status bits, + ** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15 + */ + struct msm_endpoint ept[32]; + + int *phy_init_seq; + void (*phy_reset)(void); + + struct work_struct work; + unsigned phy_status; + unsigned phy_fail_count; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + +#define ep0out ept[0] +#define ep0in ept[16] + + struct clk *clk; + struct clk *pclk; +}; + +static const struct usb_ep_ops msm72k_ep_ops; + + +static void flush_endpoint(struct msm_endpoint *ept); + +#if 0 +static unsigned ulpi_read(struct usb_info *ui, unsigned reg) +{ + unsigned timeout = 100000; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) + ; + + if (timeout == 0) { + ERROR("ulpi_read: timeout %08x\n", readl(USB_ULPI_VIEWPORT)); + return 0xffffffff; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} +#endif + +static void ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) +{ + unsigned timeout = 10000; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) + ; + + if (timeout == 0) + ERROR("ulpi_write: timeout\n"); +} + +static void ulpi_init(struct usb_info *ui) +{ + int *seq = ui->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + INFO("ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]); + ulpi_write(ui, seq[0], seq[1]); + seq += 2; + } +} + +static void init_endpoints(struct usb_info *ui) +{ + unsigned n; + + for (n = 0; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + + ept->ui = ui; + ept->bit = n; + ept->num = n & 15; + ept->ep.name = ep_name[n]; + ept->ep.ops = &msm72k_ep_ops; + + if (ept->bit > 15) { + /* IN endpoint */ + ept->head = ui->head + (ept->num << 1) + 1; + ept->flags = EPT_FLAG_IN; + } else { + /* OUT endpoint */ + ept->head = ui->head + (ept->num << 1); + ept->flags = 0; + } + + } +} + +static void configure_endpoints(struct usb_info *ui) +{ + unsigned n; + unsigned cfg; + + for (n = 0; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + + cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT; + + if (ept->bit == 0) + /* ep0 out needs interrupt-on-setup */ + cfg |= CONFIG_IOS; + + ept->head->config = cfg; + ept->head->next = TERMINATE; + + if (ept->ep.maxpacket) + INFO("ept #%d %s max:%d head:%p bit:%d\n", + ept->num, + (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->ep.maxpacket, ept->head, ept->bit); + } +} + +struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept, + unsigned bufsize, gfp_t gfp_flags) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + goto fail1; + + req->item = dma_pool_alloc(ui->pool, gfp_flags, &req->item_dma); + if (!req->item) + goto fail2; + + if (bufsize) { + req->req.buf = kmalloc(bufsize, gfp_flags); + if (!req->req.buf) + goto fail3; + req->alloced = 1; + } + + return &req->req; + +fail3: + dma_pool_free(ui->pool, req->item, req->item_dma); +fail2: + kfree(req); +fail1: + return 0; +} + +static void do_free_req(struct usb_info *ui, struct msm_request *req) +{ + if (req->alloced) + kfree(req->req.buf); + + dma_pool_free(ui->pool, req->item, req->item_dma); + kfree(req); +} + + +static void usb_ept_enable(struct msm_endpoint *ept, int yes) +{ + struct usb_info *ui = ept->ui; + int in = ept->flags & EPT_FLAG_IN; + unsigned n; + + n = readl(USB_ENDPTCTRL(ept->num)); + + if (in) { + n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_BULK; + if (yes) + n |= CTRL_TXE | CTRL_TXR; + else + n &= (~CTRL_TXE); + } else { + n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_BULK; + if (yes) + n |= CTRL_RXE | CTRL_RXR; + else + n &= ~(CTRL_RXE); + } + writel(n, USB_ENDPTCTRL(ept->num)); + +#if 1 + INFO("ept %d %s %s\n", + ept->num, in ? "in" : "out", yes ? "enabled" : "disabled"); +#endif +} + +static void usb_ept_start(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req = ept->req; + + BUG_ON(req->live); + + /* link the hw queue head to the request's transaction item */ + ept->head->next = req->item_dma; + ept->head->info = 0; + + /* start the endpoint */ + writel(1 << ept->bit, USB_ENDPTPRIME); + + /* mark this chain of requests as live */ + while (req) { + req->live = 1; + req = req->next; + } +} + +int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req) +{ + unsigned long flags; + struct msm_request *req = to_msm_request(_req); + struct msm_request *last; + struct usb_info *ui = ept->ui; + struct ept_queue_item *item = req->item; + unsigned length = req->req.length; + + if (length > 0x4000) + return -EMSGSIZE; + + spin_lock_irqsave(&ui->lock, flags); + + if (req->busy) { + req->req.status = -EBUSY; + spin_unlock_irqrestore(&ui->lock, flags); + INFO("usb_ept_queue_xfer() tried to queue busy request\n"); + return -EBUSY; + } + + if (!ui->online && (ept->num != 0)) { + req->req.status = -ESHUTDOWN; + spin_unlock_irqrestore(&ui->lock, flags); + INFO("usb_ept_queue_xfer() called while offline\n"); + return -ESHUTDOWN; + } + + req->busy = 1; + req->live = 0; + req->next = 0; + req->req.status = -EBUSY; + + req->dma = dma_map_single(NULL, req->req.buf, length, + (ept->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + /* prepare the transaction descriptor item for the hardware */ + item->next = TERMINATE; + item->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE; + item->page0 = req->dma; + item->page1 = (req->dma + 0x1000) & 0xfffff000; + item->page2 = (req->dma + 0x2000) & 0xfffff000; + item->page3 = (req->dma + 0x3000) & 0xfffff000; + + /* Add the new request to the end of the queue */ + last = ept->last; + if (last) { + /* Already requests in the queue. add us to the + * end, but let the completion interrupt actually + * start things going, to avoid hw issues + */ + last->next = req; + + /* only modify the hw transaction next pointer if + * that request is not live + */ + if (!last->live) + last->item->next = req->item_dma; + } else { + /* queue was empty -- kick the hardware */ + ept->req = req; + usb_ept_start(ept); + } + ept->last = req; + + spin_unlock_irqrestore(&ui->lock, flags); + return 0; +} + +/* --- endpoint 0 handling --- */ + +static void ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_request *r = to_msm_request(req); + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + + req->complete = r->gadget_complete; + r->gadget_complete = 0; + if (req->complete) + req->complete(&ui->ep0in.ep, req); +} + +static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_endpoint *ept = to_msm_endpoint(ep); + + /* queue up the receive of the ACK response from the host */ + if (req->status == 0) { + struct usb_info *ui = ept->ui; + req->length = 0; + req->complete = ep0_complete; + usb_ept_queue_xfer(&ui->ep0out, req); + } else + ep0_complete(ep, req); +} + +static void ep0_setup_ack(struct usb_info *ui) +{ + struct usb_request *req = ui->setup_req; + req->length = 0; + req->complete = 0; + usb_ept_queue_xfer(&ui->ep0in, req); +} + +static void ep0_setup_stall(struct usb_info *ui) +{ + writel((1<<16) | (1<<0), USB_ENDPTCTRL(0)); +} + +static void ep0_setup_send(struct usb_info *ui, unsigned length) +{ + struct usb_request *req = ui->setup_req; + struct msm_request *r = to_msm_request(req); + struct msm_endpoint *ept = &ui->ep0in; + + req->length = length; + req->complete = ep0_queue_ack_complete; + r->gadget_complete = 0; + usb_ept_queue_xfer(ept, req); +} + +static void handle_setup(struct usb_info *ui) +{ + struct usb_ctrlrequest ctl; + struct usb_request *req = ui->setup_req; + int ret; + + memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl)); + writel(EPT_RX(0), USB_ENDPTSETUPSTAT); + + /* any pending ep0 transactions must be canceled */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + + INFO("setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n", + ctl.bRequestType, ctl.bRequest, ctl.wValue, + ctl.wIndex, ctl.wLength); + + if (ctl.bRequestType == (USB_DIR_IN | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_GET_STATUS) { + if (ctl.wLength == 2) { + memset(req->buf, 0, 2); + ep0_setup_send(ui, 2); + return; + } + } + } + if (ctl.bRequestType == + (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) { + if (ctl.bRequest == USB_REQ_CLEAR_FEATURE) { + if ((ctl.wValue == 0) && (ctl.wLength == 0)) { + unsigned num = ctl.wIndex & 0x0f; + + if (num != 0) { + if (ctl.wIndex & 0x80) + num += 16; + + usb_ept_enable(ui->ept + num, 1); + ep0_setup_ack(ui); + return; + } + } + } + } + if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) + ui->online = !!ctl.wValue; + else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { + /* write address delayed (will take effect + ** after the next IN txn) + */ + writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR); + goto ack; + } + } + + /* delegate if we get here */ + if (ui->driver) { + ret = ui->driver->setup(&ui->gadget, &ctl); + if (ret >= 0) + return; + } + + /* stall ep0 on error */ + ep0_setup_stall(ui); + return; + +ack: + ep0_setup_ack(ui); +} + +static void handle_endpoint(struct usb_info *ui, unsigned bit) +{ + struct msm_endpoint *ept = ui->ept + bit; + struct msm_request *req; + unsigned long flags; + unsigned info; + + /* + INFO("handle_endpoint() %d %s req=%p(%08x)\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->req, ept->req ? ept->req->item_dma : 0); + */ + + /* expire all requests that are no longer active */ + spin_lock_irqsave(&ui->lock, flags); + while ((req = ept->req)) { + info = req->item->info; + + /* if we've processed all live requests, time to + * restart the hardware on the next non-live request + */ + if (!req->live) { + usb_ept_start(ept); + break; + } + + /* if the transaction is still in-flight, stop here */ + if (info & INFO_ACTIVE) + break; + + /* advance ept queue to the next request */ + ept->req = req->next; + if (ept->req == 0) + ept->last = 0; + + dma_unmap_single(NULL, req->dma, req->req.length, + (ept->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TXN_ERROR)) { + /* XXX pass on more specific error code */ + req->req.status = -EIO; + req->req.actual = 0; + INFO("msm72k_udc: ept %d %s error. info=%08x\n", + ept->num, + (ept->flags & EPT_FLAG_IN) ? "in" : "out", + info); + } else { + req->req.status = 0; + req->req.actual = + req->req.length - ((info >> 16) & 0x7FFF); + } + req->busy = 0; + req->live = 0; + if (req->dead) + do_free_req(ui, req); + + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void flush_endpoint_hw(struct usb_info *ui, unsigned bits) +{ + /* flush endpoint, canceling transactions + ** - this can take a "large amount of time" (per databook) + ** - the flush can fail in some cases, thus we check STAT + ** and repeat if we're still operating + ** (does the fact that this doesn't use the tripwire matter?!) + */ + do { + writel(bits, USB_ENDPTFLUSH); + while (readl(USB_ENDPTFLUSH) & bits) + udelay(100); + } while (readl(USB_ENDPTSTAT) & bits); +} + +static void flush_endpoint_sw(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req; + unsigned long flags; + + /* inactive endpoints have nothing to do here */ + if (ept->ep.maxpacket == 0) + return; + + /* put the queue head in a sane state */ + ept->head->info = 0; + ept->head->next = TERMINATE; + + /* cancel any pending requests */ + spin_lock_irqsave(&ui->lock, flags); + req = ept->req; + ept->req = 0; + ept->last = 0; + while (req != 0) { + req->busy = 0; + req->live = 0; + req->req.status = -ECONNRESET; + req->req.actual = 0; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + if (req->dead) + do_free_req(ui, req); + req = req->next; + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void flush_endpoint(struct msm_endpoint *ept) +{ + flush_endpoint_hw(ept->ui, (1 << ept->bit)); + flush_endpoint_sw(ept); +} + +static void flush_all_endpoints(struct usb_info *ui) +{ + unsigned n; + + flush_endpoint_hw(ui, 0xffffffff); + + for (n = 0; n < 32; n++) + flush_endpoint_sw(ui->ept + n); +} + + +static irqreturn_t usb_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + unsigned n; + + n = readl(USB_USBSTS); + writel(n, USB_USBSTS); + + /* somehow we got an IRQ while in the reset sequence: ignore it */ + if (ui->running == 0) + return IRQ_HANDLED; + + if (n & STS_PCI) { + switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) { + case PORTSC_PSPD_FS: + INFO("msm72k_udc: portchange USB_SPEED_FULL\n"); + ui->gadget.speed = USB_SPEED_FULL; + break; + case PORTSC_PSPD_LS: + INFO("msm72k_udc: portchange USB_SPEED_LOW\n"); + ui->gadget.speed = USB_SPEED_LOW; + break; + case PORTSC_PSPD_HS: + INFO("msm72k_udc: portchange USB_SPEED_HIGH\n"); + ui->gadget.speed = USB_SPEED_HIGH; + break; + } + } + + if (n & STS_URI) { + INFO("msm72k_udc: reset\n"); + + writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); + writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); + writel(0xffffffff, USB_ENDPTFLUSH); + writel(0, USB_ENDPTCTRL(1)); + + if (ui->online != 0) { + /* marking us offline will cause ept queue attempts + ** to fail + */ + ui->online = 0; + + flush_all_endpoints(ui); + + /* XXX: we can't seem to detect going offline, + * XXX: so deconfigure on reset for the time being + */ + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + } + } + + if (n & STS_SLI) + INFO("msm72k_udc: suspend\n"); + + if (n & STS_UI) { + n = readl(USB_ENDPTSETUPSTAT); + if (n & EPT_RX(0)) + handle_setup(ui); + + n = readl(USB_ENDPTCOMPLETE); + writel(n, USB_ENDPTCOMPLETE); + while (n) { + unsigned bit = __ffs(n); + handle_endpoint(ui, bit); + n = n & (~(1 << bit)); + } + } + return IRQ_HANDLED; +} + +static void usb_prepare(struct usb_info *ui) +{ + spin_lock_init(&ui->lock); + + memset(ui->buf, 0, 4096); + ui->head = (void *) (ui->buf + 0); + + /* only important for reset/reinit */ + memset(ui->ept, 0, sizeof(ui->ept)); + ui->next_item = 0; + ui->next_ifc_num = 0; + + init_endpoints(ui); + + ui->ep0in.ep.maxpacket = 64; + ui->ep0out.ep.maxpacket = 64; + + ui->setup_req = + usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE, GFP_KERNEL); + + INIT_WORK(&ui->work, usb_do_work); +} + +static void usb_suspend_phy(struct usb_info *ui) +{ + /* clear VBusValid and SessionEnd rising interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f); + /* clear VBusValid and SessionEnd falling interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x12); + /* disable interface protect circuit to drop current consumption */ + ulpi_write(ui, (1 << 7), 0x08); + /* clear the SuspendM bit -> suspend the PHY */ + ulpi_write(ui, 1 << 6, 0x06); +} + +static void usb_reset(struct usb_info *ui) +{ + unsigned long flags; + INFO("msm72k_udc: reset controller\n"); + + spin_lock_irqsave(&ui->lock, flags); + ui->running = 0; + spin_unlock_irqrestore(&ui->lock, flags); + +#if 0 + /* we should flush and shutdown cleanly if already running */ + writel(0xffffffff, USB_ENDPTFLUSH); + msleep(2); +#endif + + /* RESET */ + writel(2, USB_USBCMD); + msleep(10); + + if (ui->phy_reset) + ui->phy_reset(); + + /* INCR4 BURST mode */ + writel(0x01, USB_SBUSCFG); + + /* select DEVICE mode */ + writel(0x12, USB_USBMODE); + msleep(1); + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + ulpi_init(ui); + + writel(ui->dma, USB_ENDPOINTLISTADDR); + + configure_endpoints(ui); + + /* marking us offline will cause ept queue attempts to fail */ + ui->online = 0; + + /* terminate any pending transactions */ + flush_all_endpoints(ui); + + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + /* enable interrupts */ + writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); + + /* go to RUN mode (D+ pullup enable) */ + writel(0x00080001, USB_USBCMD); + + spin_lock_irqsave(&ui->lock, flags); + ui->running = 1; + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void usb_start(struct usb_info *ui) +{ + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_START; + schedule_work(&ui->work); + spin_unlock_irqrestore(&ui->lock, flags); +} + +static struct usb_info *the_usb_info; + +static int usb_free(struct usb_info *ui, int ret) +{ + INFO("usb_free(%d)\n", ret); + + if (ui->irq) + free_irq(ui->irq, 0); + if (ui->pool) + dma_pool_destroy(ui->pool); + if (ui->dma) + dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma); + if (ui->addr) + iounmap(ui->addr); + if (ui->clk) + clk_put(ui->clk); + if (ui->pclk) + clk_put(ui->pclk); + kfree(ui); + return ret; +} + +static void usb_do_work_check_vbus(struct usb_info *ui) +{ + unsigned long iflags; + + spin_lock_irqsave(&ui->lock, iflags); + if (vbus) + ui->flags |= USB_FLAG_VBUS_ONLINE; + else + ui->flags |= USB_FLAG_VBUS_OFFLINE; + spin_unlock_irqrestore(&ui->lock, iflags); +} + +static void usb_do_work(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, work); + unsigned long iflags; + unsigned flags, _vbus; + + for (;;) { + spin_lock_irqsave(&ui->lock, iflags); + flags = ui->flags; + ui->flags = 0; + _vbus = vbus; + spin_unlock_irqrestore(&ui->lock, iflags); + + /* give up if we have nothing to do */ + if (flags == 0) + break; + + switch (ui->state) { + case USB_STATE_IDLE: + if (flags & USB_FLAG_START) { + pr_info("msm72k_udc: IDLE -> ONLINE\n"); + clk_enable(ui->clk); + clk_enable(ui->pclk); + usb_reset(ui); + + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + } + break; + case USB_STATE_ONLINE: + /* If at any point when we were online, we received + * the signal to go offline, we must honor it + */ + if (flags & USB_FLAG_VBUS_OFFLINE) { + pr_info("msm72k_udc: ONLINE -> OFFLINE\n"); + + /* synchronize with irq context */ + spin_lock_irqsave(&ui->lock, iflags); + ui->running = 0; + ui->online = 0; + spin_unlock_irqrestore(&ui->lock, iflags); + + /* terminate any transactions, etc */ + flush_all_endpoints(ui); + + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + /* power down phy, clock down usb */ + spin_lock_irqsave(&ui->lock, iflags); + usb_suspend_phy(ui); + clk_disable(ui->pclk); + clk_disable(ui->clk); + spin_unlock_irqrestore(&ui->lock, iflags); + + ui->state = USB_STATE_OFFLINE; + usb_do_work_check_vbus(ui); + break; + } + if (flags & USB_FLAG_RESET) { + pr_info("msm72k_udc: ONLINE -> RESET\n"); + usb_reset(ui); + pr_info("msm72k_udc: RESET -> ONLINE\n"); + break; + } + break; + case USB_STATE_OFFLINE: + /* If we were signaled to go online and vbus is still + * present when we received the signal, go online. + */ + if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) { + pr_info("msm72k_udc: OFFLINE -> ONLINE\n"); + clk_enable(ui->clk); + clk_enable(ui->pclk); + usb_reset(ui); + + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + } + break; + } + } +} + +/* FIXME - the callers of this function should use a gadget API instead. + * This is called from htc_battery.c and board-halibut.c + * WARNING - this can get called before this driver is initialized. + */ +void msm_hsusb_set_vbus_state(int online) +{ + unsigned long flags; + struct usb_info *ui = the_usb_info; + + if (ui) { + spin_lock_irqsave(&ui->lock, flags); + if (vbus != online) { + vbus = online; + if (online) + ui->flags |= USB_FLAG_VBUS_ONLINE; + else + ui->flags |= USB_FLAG_VBUS_OFFLINE; + schedule_work(&ui->work); + } + spin_unlock_irqrestore(&ui->lock, flags); + } else { + printk(KERN_ERR "msm_hsusb_set_vbus_state called before driver initialized\n"); + vbus = online; + } +} + +#if defined(CONFIG_DEBUG_FS) + +void usb_function_reenumerate(void) +{ + struct usb_info *ui = the_usb_info; + + /* disable and re-enable the D+ pullup */ + INFO("msm72k_udc: disable pullup\n"); + writel(0x00080000, USB_USBCMD); + + msleep(10); + + INFO("msm72k_udc: enable pullup\n"); + writel(0x00080001, USB_USBCMD); +} + +static char debug_buffer[PAGE_SIZE]; + +static ssize_t debug_read_status(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + char *buf = debug_buffer; + unsigned long flags; + struct msm_endpoint *ept; + struct msm_request *req; + int n; + int i = 0; + + spin_lock_irqsave(&ui->lock, flags); + + i += scnprintf(buf + i, PAGE_SIZE - i, + "regs: setup=%08x prime=%08x stat=%08x done=%08x\n", + readl(USB_ENDPTSETUPSTAT), + readl(USB_ENDPTPRIME), + readl(USB_ENDPTSTAT), + readl(USB_ENDPTCOMPLETE)); + i += scnprintf(buf + i, PAGE_SIZE - i, + "regs: cmd=%08x sts=%08x intr=%08x port=%08x\n\n", + readl(USB_USBCMD), + readl(USB_USBSTS), + readl(USB_USBINTR), + readl(USB_PORTSC)); + + + for (n = 0; n < 32; n++) { + ept = ui->ept + n; + if (ept->ep.maxpacket == 0) + continue; + + i += scnprintf(buf + i, PAGE_SIZE - i, + "ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out", + ept->head->config, ept->head->active, + ept->head->next, ept->head->info); + + for (req = ept->req; req; req = req->next) + i += scnprintf(buf + i, PAGE_SIZE - i, + " req @%08x next=%08x info=%08x page0=%08x %c %c\n", + req->item_dma, req->item->next, + req->item->info, req->item->page0, + req->busy ? 'B' : ' ', + req->live ? 'L' : ' '); + } + + i += scnprintf(buf + i, PAGE_SIZE - i, + "phy failure count: %d\n", ui->phy_fail_count); + + spin_unlock_irqrestore(&ui->lock, flags); + + return simple_read_from_buffer(ubuf, count, ppos, buf, i); +} + +static ssize_t debug_write_reset(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_RESET; + schedule_work(&ui->work); + spin_unlock_irqrestore(&ui->lock, flags); + + return count; +} + +static ssize_t debug_write_cycle(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + usb_function_reenumerate(); + return count; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +const struct file_operations debug_stat_ops = { + .open = debug_open, + .read = debug_read_status, +}; + +const struct file_operations debug_reset_ops = { + .open = debug_open, + .write = debug_write_reset, +}; + +const struct file_operations debug_cycle_ops = { + .open = debug_open, + .write = debug_write_cycle, +}; + +static void usb_debugfs_init(struct usb_info *ui) +{ + struct dentry *dent; + dent = debugfs_create_dir("usb", 0); + if (IS_ERR(dent)) + return; + + debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops); + debugfs_create_file("reset", 0222, dent, ui, &debug_reset_ops); + debugfs_create_file("cycle", 0222, dent, ui, &debug_cycle_ops); +} +#else +static void usb_debugfs_init(struct usb_info *ui) {} +#endif + +static int +msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + + _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + usb_ept_enable(ept, 1); + return 0; +} + +static int msm72k_disable(struct usb_ep *_ep) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + + usb_ept_enable(ept, 0); + return 0; +} + +static struct usb_request * +msm72k_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + return usb_ept_alloc_req(to_msm_endpoint(_ep), 0, gfp_flags); +} + +static void +msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct msm_request *req = to_msm_request(_req); + struct msm_endpoint *ept = to_msm_endpoint(_ep); + struct usb_info *ui = ept->ui; + unsigned long flags; + int dead = 0; + + spin_lock_irqsave(&ui->lock, flags); + /* defer freeing resources if request is still busy */ + if (req->busy) + dead = req->dead = 1; + spin_unlock_irqrestore(&ui->lock, flags); + + /* if req->dead, then we will clean up when the request finishes */ + if (!dead) + do_free_req(ui, req); +} + +static int +msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags) +{ + struct msm_endpoint *ep = to_msm_endpoint(_ep); + struct usb_info *ui = ep->ui; + + if (ep == &ui->ep0in) { + struct msm_request *r = to_msm_request(req); + r->gadget_complete = req->complete; + /* ep0_queue_ack_complete queue a receive for ACK before + ** calling req->complete + */ + req->complete = ep0_queue_ack_complete; + } + return usb_ept_queue_xfer(ep, req); +} + +static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct msm_endpoint *ep = to_msm_endpoint(_ep); + struct msm_request *req = to_msm_request(_req); + struct usb_info *ui = ep->ui; + + struct msm_request *cur, *prev; + unsigned long flags; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ui->lock, flags); + cur = ep->req; + prev = NULL; + + while (cur != 0) { + if (cur == req) { + req->busy = 0; + req->live = 0; + req->req.status = -ECONNRESET; + req->req.actual = 0; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ep->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + if (req->dead) + do_free_req(ui, req); + /* remove from linked list */ + if (prev) + prev->next = cur->next; + else + ep->req = cur->next; + prev = cur; + /* break from loop */ + cur = NULL; + } else + cur = cur->next; + } + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; +} + +static int +msm72k_set_halt(struct usb_ep *_ep, int value) +{ + return -EOPNOTSUPP; +} + +static int +msm72k_fifo_status(struct usb_ep *_ep) +{ + return -EOPNOTSUPP; +} + +static void +msm72k_fifo_flush(struct usb_ep *_ep) +{ + flush_endpoint(to_msm_endpoint(_ep)); +} + +static const struct usb_ep_ops msm72k_ep_ops = { + .enable = msm72k_enable, + .disable = msm72k_disable, + + .alloc_request = msm72k_alloc_request, + .free_request = msm72k_free_request, + + .queue = msm72k_queue, + .dequeue = msm72k_dequeue, + + .set_halt = msm72k_set_halt, + .fifo_status = msm72k_fifo_status, + .fifo_flush = msm72k_fifo_flush, +}; + +static int msm72k_get_frame(struct usb_gadget *_gadget) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + + /* frame number is in bits 13:3 */ + return (readl(USB_FRINDEX) >> 3) & 0x000007FF; +} + +/* VBUS reporting logically comes from a transceiver */ +static int msm72k_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + msm_hsusb_set_vbus_state(is_active); + return 0; +} + +/* drivers may have software control over D+ pullup */ +static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + + if (is_active) + writel(0x00080001, USB_USBCMD); + else + writel(0x00080000, USB_USBCMD); + + return 0; +} + +static const struct usb_gadget_ops msm72k_ops = { + .get_frame = msm72k_get_frame, + .vbus_session = msm72k_udc_vbus_session, + .pullup = msm72k_pullup, +}; + +static int msm72k_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_info *ui; + int irq; + int ret; + + INFO("msm72k_probe\n"); + ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL); + if (!ui) + return -ENOMEM; + + ui->pdev = pdev; + + if (pdev->dev.platform_data) { + struct msm_hsusb_platform_data *pdata = pdev->dev.platform_data; + ui->phy_reset = pdata->phy_reset; + ui->phy_init_seq = pdata->phy_init_seq; + } + + irq = platform_get_irq(pdev, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res || (irq < 0)) + return usb_free(ui, -ENODEV); + + ui->addr = ioremap(res->start, 4096); + if (!ui->addr) + return usb_free(ui, -ENOMEM); + + ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL); + if (!ui->buf) + return usb_free(ui, -ENOMEM); + + ui->pool = dma_pool_create("msm72k_udc", NULL, 32, 32, 0); + if (!ui->pool) + return usb_free(ui, -ENOMEM); + + INFO("msm72k_probe() io=%p, irq=%d, dma=%p(%x)\n", + ui->addr, irq, ui->buf, ui->dma); + + ui->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(ui->clk)) + return usb_free(ui, PTR_ERR(ui->clk)); + + ui->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(ui->pclk)) + return usb_free(ui, PTR_ERR(ui->pclk)); + + ret = request_irq(irq, usb_interrupt, 0, pdev->name, ui); + if (ret) + return usb_free(ui, ret); + enable_irq_wake(irq); + ui->irq = irq; + + ui->gadget.ops = &msm72k_ops; + ui->gadget.is_dualspeed = 1; + device_initialize(&ui->gadget.dev); + strcpy(ui->gadget.dev.bus_id, "gadget"); + ui->gadget.dev.parent = &pdev->dev; + ui->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_usb_info = ui; + + usb_debugfs_init(ui); + + usb_prepare(ui); + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct usb_info *ui = the_usb_info; + int retval, n; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!ui) + return -ENODEV; + if (ui->driver) + return -EBUSY; + + /* first hook up the driver ... */ + ui->driver = driver; + ui->gadget.dev.driver = &driver->driver; + ui->gadget.name = driver_name; + INIT_LIST_HEAD(&ui->gadget.ep_list); + ui->gadget.ep0 = &ui->ep0in.ep; + INIT_LIST_HEAD(&ui->gadget.ep0->ep_list); + ui->gadget.speed = USB_SPEED_UNKNOWN; + + for (n = 1; n < 16; n++) { + struct msm_endpoint *ept = ui->ept + n; + list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list); + ept->ep.maxpacket = 512; + } + for (n = 17; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list); + ept->ep.maxpacket = 512; + } + + retval = device_add(&ui->gadget.dev); + if (retval) + goto fail; + + retval = driver->bind(&ui->gadget); + if (retval) { + INFO("bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del(&ui->gadget.dev); + goto fail; + } + + INFO("msm72k_udc: registered gadget driver '%s'\n", + driver->driver.name); + usb_start(ui); + + return 0; + +fail: + ui->driver = NULL; + ui->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_info *dev = the_usb_info; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver || !driver->unbind) + return -EINVAL; + + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + device_del(&dev->gadget.dev); + + VDEBUG("unregistered gadget driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +static struct platform_driver usb_driver = { + .probe = msm72k_probe, + .driver = { .name = "msm_hsusb", }, +}; + +static int __init init(void) +{ + return platform_driver_register(&usb_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + platform_driver_unregister(&usb_driver); +} +module_exit(cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Mike Lockwood, Brian Swetland"); +MODULE_LICENSE("GPL");