* [PATCH] Adding usb gadget support
@ 2010-07-15 17:31 David Lanzendörfer
2010-07-15 17:49 ` Michael Bohan
2010-07-16 6:07 ` Pavan Kondeti
0 siblings, 2 replies; 10+ messages in thread
From: David Lanzendörfer @ 2010-07-15 17:31 UTC (permalink / raw)
To: linux-arm-msm
Hi
I wanna propose the introduction of usb gadget for msm72k into
https://www.codeaurora.org/gitweb/quic/kernel/?p=dwalker/linux-msm.git;a=tree;h=refs/heads/for-next;hb=for-next
sadly the following patch seems to have some flaws.
I finds the internal chip and creates a iface usb0, but doesnt get up and online.
I do not get an usb0 popping up on hostside either.
Someone an idea how to fix this code?
best regards
leviathan
---
arch/arm/mach-msm/include/mach/msm_hsusb.h | 74 ++
arch/arm/mach-msm/include/mach/msm_hsusb_hw.h | 204 +++
drivers/usb/gadget/Kconfig | 17 +
drivers/usb/gadget/Makefile | 1 +
drivers/usb/gadget/msm72k_udc.c | 1738 +++++++++++++++++++++++++
5 files changed, 2034 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-msm/include/mach/msm_hsusb.h
create mode 100644 arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
create mode 100644 drivers/usb/gadget/msm72k_udc.c
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h
new file mode 100644
index 0000000..2d49b0f
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h
@@ -0,0 +1,74 @@
+/* linux/include/asm-arm/arch-msm/hsusb.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * 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 __ASM_ARCH_MSM_HSUSB_H
+#define __ASM_ARCH_MSM_HSUSB_H
+
+#include <linux/types.h>
+
+/* 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 */
+ __u16 product_id;
+
+ /* bit mask of enabled usb_functions, matching ordering
+ ** in msm_hsusb_platform_data.functions
+ */
+ __u32 functions;
+};
+#endif
+
+struct msm_hsusb_platform_data {
+ /* hard reset the ULPI PHY */
+ void (*phy_reset)(void);
+
+ /* for notification when USB is connected or disconnected */
+ void (*usb_connected)(int);
+
+ /* val, reg pairs terminated by -1 */
+ int *phy_init_seq;
+
+#ifdef CONFIG_USB_FUNCTION
+ /* USB device descriptor fields */
+ __u16 vendor_id;
+
+ /* Default product ID.
+ ** This can be overridden dynamically based on the disabled
+ ** state of the functions using the product_table.
+ */
+ __u16 product_id;
+
+ __u16 version;
+ char *serial_number;
+ char *product_name;
+ char *manufacturer_name;
+
+ /* list of function drivers to bind to this configuration */
+ int num_functions;
+ char **functions;
+
+ /* if num_products is zero, then the default value in product_id
+ ** is used for the configuration descriptor.
+ */
+ int num_products;
+ struct msm_hsusb_product *products;
+#endif
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
new file mode 100644
index 0000000..8042b31
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * 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_TXT_EP_TYPE_SHIFT 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 CTRL_RXT_EP_TYPE_SHIFT 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)
+/* suspend and remote wakeup */
+#define PORTSC_FPR (1 << 6)
+#define PORTSC_SUSP (1 << 7)
+
+/* test mode support */
+#define J_TEST (0x0100)
+#define K_TEST (0x0200)
+#define SE0_NAK_TEST (0x0300)
+#define TST_PKT_TEST (0x0400)
+#define PORTSC_PTC (0xf << 16)
+#define PORTSC_PTC_J_STATE (0x01 << 16)
+#define PORTSC_PTC_K_STATE (0x02 << 16)
+#define PORTSC_PTC_SE0_NAK (0x03 << 16)
+#define PORTSC_PTC_TST_PKT (0x04 << 16)
+#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 591ae9f..18d28c2 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -509,6 +509,23 @@ config USB_LANGWELL
#
# LAST -- dummy/emulated controller
#
+config USB_GADGET_MSM_72K
+ boolean "MSM 72K Device Controller"
+ depends on ARCH_MSM
+ 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)"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9bcde11..48ea27a 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
+obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
new file mode 100644
index 0000000..1630cf8
--- /dev/null
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -0,0 +1,1738 @@
+/*
+ * Driver for HighSpeed USB Client Controller in MSM7K
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ * Brian Swetland <swetland@google.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board.h>
+#include <mach/msm_hsusb.h>
+#include <linux/device.h>
+#include <mach/msm_hsusb_hw.h>
+
+static const char driver_name[] = "msm72k_udc";
+
+/* #define DEBUG */
+/* #define VERBOSE */
+
+#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);
+
+ /* for notification when USB is connected or disconnected */
+ void (*usb_connected)(int);
+
+ 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;
+
+ unsigned int ep0_dir;
+ u16 test_mode;
+
+ u8 remote_wakeup;
+};
+
+static const struct usb_ep_ops msm72k_ep_ops;
+
+
+static int msm72k_pullup(struct usb_gadget *_gadget, int is_active);
+static int msm72k_set_halt(struct usb_ep *_ep, int value);
+static void flush_endpoint(struct msm_endpoint *ept);
+
+static int usb_ep_get_stall(struct msm_endpoint *ept)
+{
+ unsigned int n;
+ struct usb_info *ui = ept->ui;
+
+ n = readl(USB_ENDPTCTRL(ept->num));
+ if (ept->flags & EPT_FLAG_IN)
+ return (CTRL_TXS & n) ? 1 : 0;
+ else
+ return (CTRL_RXS & n) ? 1 : 0;
+}
+
+#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 config_ept(struct msm_endpoint *ept)
+{
+ unsigned 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);
+}
+
+static void configure_endpoints(struct usb_info *ui)
+{
+ unsigned n;
+
+ for (n = 0; n < 32; n++)
+ config_ept(ui->ept + n);
+}
+
+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,
+ unsigned char ep_type)
+{
+ struct usb_info *ui = ept->ui;
+ int in = ept->flags & EPT_FLAG_IN;
+ unsigned n;
+
+ n = readl(USB_ENDPTCTRL(ept->num));
+
+ if (in) {
+ if (yes) {
+ n = (n & (~CTRL_TXT_MASK)) |
+ (ep_type << CTRL_TXT_EP_TYPE_SHIFT);
+ n |= CTRL_TXE | CTRL_TXR;
+ } else
+ n &= (~CTRL_TXE);
+ } else {
+ if (yes) {
+ n = (n & (~CTRL_RXT_MASK)) |
+ (ep_type << CTRL_RXT_EP_TYPE_SHIFT);
+ 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;
+ if (ui->ep0_dir == USB_DIR_IN)
+ usb_ept_queue_xfer(&ui->ep0out, req);
+ else
+ usb_ept_queue_xfer(&ui->ep0in, req);
+ } else
+ ep0_complete(ep, req);
+}
+
+static void ep0_setup_ack_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(ep);
+ struct usb_info *ui = ept->ui;
+ unsigned int temp;
+
+ if (!ui->test_mode)
+ return;
+
+ switch (ui->test_mode) {
+ case J_TEST:
+ pr_info("usb electrical test mode: (J)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC);
+ break;
+
+ case K_TEST:
+ pr_info("usb electrical test mode: (K)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_K_STATE, USB_PORTSC);
+ break;
+
+ case SE0_NAK_TEST:
+ pr_info("usb electrical test mode: (SE0-NAK)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_SE0_NAK, USB_PORTSC);
+ break;
+
+ case TST_PKT_TEST:
+ pr_info("usb electrical test mode: (TEST_PKT)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_TST_PKT, USB_PORTSC);
+ break;
+ }
+}
+
+static void ep0_setup_ack(struct usb_info *ui)
+{
+ struct usb_request *req = ui->setup_req;
+ req->length = 0;
+ req->complete = ep0_setup_ack_complete;
+ 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);
+
+ if (ctl.bRequestType & USB_DIR_IN)
+ ui->ep0_dir = USB_DIR_IN;
+ else
+ ui->ep0_dir = USB_DIR_OUT;
+
+ /* 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_MASK)) ==
+ (USB_DIR_IN | USB_TYPE_STANDARD)) {
+ if (ctl.bRequest == USB_REQ_GET_STATUS) {
+ if (ctl.wLength != 2)
+ goto stall;
+ switch (ctl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_ENDPOINT:
+ {
+ struct msm_endpoint *ept;
+ unsigned num =
+ ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ u16 temp = 0;
+
+ if (num == 0) {
+ memset(req->buf, 0, 2);
+ break;
+ }
+ if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+ num += 16;
+ ept = &ui->ep0out + num;
+ temp = usb_ep_get_stall(ept);
+ temp = temp << USB_ENDPOINT_HALT;
+ memcpy(req->buf, &temp, 2);
+ break;
+ }
+ case USB_RECIP_DEVICE:
+ {
+ u16 temp = 0;
+
+ temp = 1 << USB_DEVICE_SELF_POWERED;
+ temp |= (ui->remote_wakeup <<
+ USB_DEVICE_REMOTE_WAKEUP);
+ memcpy(req->buf, &temp, 2);
+ break;
+ }
+ case USB_RECIP_INTERFACE:
+ memset(req->buf, 0, 2);
+ break;
+ default:
+ goto stall;
+ }
+ 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) ||
+ (ctl.bRequest == USB_REQ_SET_FEATURE)) {
+ if ((ctl.wValue == 0) && (ctl.wLength == 0)) {
+ unsigned num = ctl.wIndex & 0x0f;
+
+ if (num != 0) {
+ struct msm_endpoint *ept;
+
+ if (ctl.wIndex & 0x80)
+ num += 16;
+ ept = &ui->ep0out + num;
+
+ if (ctl.bRequest == USB_REQ_SET_FEATURE)
+ msm72k_set_halt(&ept->ep, 1);
+ else
+ msm72k_set_halt(&ept->ep, 0);
+ }
+ goto ack;
+ }
+ }
+ }
+ 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;
+ } else if (ctl.bRequest == USB_REQ_SET_FEATURE) {
+ switch (ctl.wValue) {
+ case USB_DEVICE_TEST_MODE:
+ switch (ctl.wIndex) {
+ case J_TEST:
+ case K_TEST:
+ case SE0_NAK_TEST:
+ case TST_PKT_TEST:
+ ui->test_mode = ctl.wIndex;
+ goto ack;
+ }
+ goto stall;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ ui->remote_wakeup = 1;
+ goto ack;
+ }
+ } else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) &&
+ (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) {
+ ui->remote_wakeup = 0;
+ goto ack;
+ }
+ }
+
+ /* delegate if we get here */
+ if (ui->driver) {
+ ret = ui->driver->setup(&ui->gadget, &ctl);
+ if (ret >= 0)
+ return;
+ }
+
+stall:
+ /* 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);
+}
+
+static void usb_reset(struct usb_info *ui)
+{
+ unsigned long flags;
+ unsigned otgsc;
+
+ 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
+
+ otgsc = readl(USB_OTGSC);
+
+ /* 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(otgsc, USB_OTGSC);
+ writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
+
+ /* go to RUN mode (D+ pullup enable) */
+ msm72k_pullup(&ui->gadget, 1);
+
+ 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;
+ msm72k_pullup(&ui->gadget, 0);
+ spin_unlock_irqrestore(&ui->lock, iflags);
+
+ if (ui->usb_connected)
+ ui->usb_connected(0);
+
+ /* 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);
+
+ if (ui->usb_connected)
+ ui->usb_connected(1);
+
+ 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.
+ */
+#if !defined(MODULE)
+void msm_hsusb_set_vbus_state(int online)
+#else
+static void msm_hsusb_set_vbus_state_new(int online)
+#endif
+{
+ 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);
+ unsigned char ep_type =
+ desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ config_ept(ept);
+ usb_ept_enable(ept, 1, ep_type);
+ return 0;
+}
+
+static int msm72k_disable(struct usb_ep *_ep)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(_ep);
+
+ usb_ept_enable(ept, 0, 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);
+ if (!req->length)
+ goto ep_queue_done;
+ r->gadget_complete = req->complete;
+ /* ep0_queue_ack_complete queue a receive for ACK before
+ ** calling req->complete
+ */
+ req->complete = ep0_queue_ack_complete;
+ if (ui->ep0_dir == USB_DIR_OUT)
+ ep = &ui->ep0out;
+ }
+ep_queue_done:
+ 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)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(_ep);
+ struct usb_info *ui = ept->ui;
+ unsigned int in = ept->flags & EPT_FLAG_IN;
+ unsigned int n;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ui->lock, flags);
+ n = readl(USB_ENDPTCTRL(ept->num));
+
+ if (in) {
+ if (value)
+ n |= CTRL_TXS;
+ else {
+ n &= ~CTRL_TXS;
+ n |= CTRL_TXR;
+ }
+ } else {
+ if (value)
+ n |= CTRL_RXS;
+ else {
+ n &= ~CTRL_RXS;
+ n |= CTRL_RXR;
+ }
+ }
+ writel(n, USB_ENDPTCTRL(ept->num));
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return 0;
+}
+
+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) {
+ if (vbus && ui->driver)
+ writel(0x00080001, USB_USBCMD);
+ } else
+ writel(0x00080000, USB_USBCMD);
+
+ return 0;
+}
+
+static int msm72k_wakeup(struct usb_gadget *_gadget)
+{
+ struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+ unsigned long flags;
+
+ if (!ui->remote_wakeup) {
+ pr_err("%s: remote wakeup not supported\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ if (!ui->online) {
+ pr_err("%s: device is not configured\n", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&ui->lock, flags);
+ if ((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) {
+ pr_info("%s: enabling force resume\n", __func__);
+ writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+ }
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops msm72k_ops = {
+ .get_frame = msm72k_get_frame,
+ .vbus_session = msm72k_udc_vbus_session,
+ .pullup = msm72k_pullup,
+ .wakeup = msm72k_wakeup,
+};
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_info *ui = the_usb_info;
+
+ msm72k_wakeup(&ui->gadget);
+
+ return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+
+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;
+ ui->usb_connected = pdata->usb_connected;
+ }
+
+ 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);
+ ui->gadget.dev.init_name="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;
+ }
+
+ /* create sysfs node for remote wakeup */
+ retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup);
+ if (retval != 0)
+ INFO("failed to create sysfs entry: (wakeup) error: (%d)\n",
+ retval);
+ 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;
+
+ device_remove_file(&dev->gadget.dev, &dev_attr_wakeup);
+ 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", },
+};
+
+#if defined(MODULE)
+extern void (*msm_hsusb_set_vbus_state)(int online);
+#endif
+
+static int __init init(void)
+{
+ #if defined(MODULE)
+ msm_hsusb_set_vbus_state = &msm_hsusb_set_vbus_state_new;
+ #endif
+ 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");
--
1.6.4.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-15 17:31 David Lanzendörfer
@ 2010-07-15 17:49 ` Michael Bohan
2010-07-15 19:05 ` David Lanzendörfer
2010-07-16 6:07 ` Pavan Kondeti
1 sibling, 1 reply; 10+ messages in thread
From: Michael Bohan @ 2010-07-15 17:49 UTC (permalink / raw)
To: David Lanzendörfer; +Cc: linux-arm-msm
On 7/15/2010 10:31 AM, David Lanzendörfer wrote:
> I wanna propose the introduction of usb gadget for msm72k into
> https://www.codeaurora.org/gitweb/quic/kernel/?p=dwalker/linux-msm.git;a=tree;h=refs/heads/for-next;hb=for-next
> sadly the following patch seems to have some flaws.
> I finds the internal chip and creates a iface usb0, but doesnt get up and online.
> I do not get an usb0 popping up on hostside either.
> Someone an idea how to fix this code?
Be weary of the phy_reset() code which must be implemented correctly and
passed in as platform_data. This code can be board dependent. Failure to
implement this correctly will result in the interface not coming online.
Michael
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-15 17:49 ` Michael Bohan
@ 2010-07-15 19:05 ` David Lanzendörfer
2010-07-15 21:46 ` David Lanzendörfer
0 siblings, 1 reply; 10+ messages in thread
From: David Lanzendörfer @ 2010-07-15 19:05 UTC (permalink / raw)
To: linux-arm-msm
[-- Attachment #1: Type: Text/Plain, Size: 302 bytes --]
>Be weary of the phy_reset() code which must be implemented correctly and passed in as platform_data.
Ok. That might be the problem.
>This code can be board dependent. Failure to implement this correctly will result in the interface not coming online.
Ok. I'll check.
thx and best regards
leviathan
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-15 19:05 ` David Lanzendörfer
@ 2010-07-15 21:46 ` David Lanzendörfer
2010-07-15 22:34 ` Michael Bohan
2010-07-15 23:08 ` Tim HRM
0 siblings, 2 replies; 10+ messages in thread
From: David Lanzendörfer @ 2010-07-15 21:46 UTC (permalink / raw)
To: linux-arm-msm
[-- Attachment #1: Type: Text/Plain, Size: 478 bytes --]
>>Be weary of the phy_reset() code which must be implemented correctly and passed in as platform_data.
>Ok. That might be the problem.
>>This code can be board dependent. Failure to implement this correctly will result in the interface not coming online.
>Ok. I'll check.
I was informed in #htc-linux that there is already a call PCOM_MSM_HSUSB_PHY_RESET provided by proc_comm.
But I do not have the lisest idea how to use that.
Someone inspiration?
best regards
leviathan
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-15 21:46 ` David Lanzendörfer
@ 2010-07-15 22:34 ` Michael Bohan
2010-07-15 23:08 ` Tim HRM
1 sibling, 0 replies; 10+ messages in thread
From: Michael Bohan @ 2010-07-15 22:34 UTC (permalink / raw)
To: David Lanzendörfer; +Cc: linux-arm-msm
On 7/15/2010 2:46 PM, David Lanzendörfer wrote:
> I was informed in #htc-linux that there is already a call PCOM_MSM_HSUSB_PHY_RESET provided by proc_comm.
> But I do not have the lisest idea how to use that.
I suggest looking at this tree which has USB support on multiple boards
already:
https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=shortlog;h=refs/heads/aosp/android-msm-2.6.32
That should make it more clear how to properly perform the reset for
your particular board. It may not necessarily be
PCOM_MSM_HSUSB_PHY_RESET. In fact, I don't think that command is
actually used for any current boards.
Michael
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-15 21:46 ` David Lanzendörfer
2010-07-15 22:34 ` Michael Bohan
@ 2010-07-15 23:08 ` Tim HRM
1 sibling, 0 replies; 10+ messages in thread
From: Tim HRM @ 2010-07-15 23:08 UTC (permalink / raw)
To: David Lanzendörfer; +Cc: linux-arm-msm
On Thu, Jul 15, 2010 at 5:46 PM, David Lanzendörfer
<david.lanzendoerfer@o2s.ch> wrote:
>>>Be weary of the phy_reset() code which must be implemented correctly and passed in as platform_data.
>>Ok. That might be the problem.
>
>>>This code can be board dependent. Failure to implement this correctly will result in the interface not coming online.
>>Ok. I'll check.
>
> I was informed in #htc-linux that there is already a call PCOM_MSM_HSUSB_PHY_RESET provided by proc_comm.
> But I do not have the lisest idea how to use that.
>
> Someone inspiration?
>
> best regards
> leviathan
>
that's still device specific, it might work on our devices but
certainly doesn't belong upstream (in for-next). maybe QuIC should
look into implementing a proper PHY like Swetland's comment said early
on, instead of the callback approach.
--
Timothy Meade
tmzt #htc-linux
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-15 17:31 David Lanzendörfer
2010-07-15 17:49 ` Michael Bohan
@ 2010-07-16 6:07 ` Pavan Kondeti
1 sibling, 0 replies; 10+ messages in thread
From: Pavan Kondeti @ 2010-07-16 6:07 UTC (permalink / raw)
To: David Lanzendörfer; +Cc: linux-arm-msm
----- "David Lanzendörfer" <david.lanzendoerfer@o2s.ch> wrote:
> Hi
> I wanna propose the introduction of usb gadget for msm72k into
> https://www.codeaurora.org/gitweb/quic/kernel/?p=dwalker/linux-msm.git;a=tree;h=refs/heads/for-next;hb=for-next
> sadly the following patch seems to have some flaws.
> I finds the internal chip and creates a iface usb0, but doesnt get up
> and online.
> I do not get an usb0 popping up on hostside either.
> Someone an idea how to fix this code?
>
What do you mean by iface usb0? Are you referring to usb0 network interface
showing up in ifconfig?
Thanks,
Pavan
--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
@ 2010-07-16 7:33 David Lanzendörfer
2010-07-16 17:14 ` Michael Bohan
0 siblings, 1 reply; 10+ messages in thread
From: David Lanzendörfer @ 2010-07-16 7:33 UTC (permalink / raw)
To: linux-arm-msm
[-- Attachment #1: Type: text/plain, Size: 571 bytes --]
Also here we are using phy_reset
https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=blob;f=drivers/usb/gadget/msm72k_udc.c;h=f3669507bcd929b043e573d08e600171cc0c87de;hb=refs/heads/aosp/android-msm-2.6.32
Its needed for cable plug/unplug event recognition as it looks like.
So we realy seem to need phy_reset.
In the android branch phy_reset() was declared inside a second devices.c file called
devices_htc.c
I considered it a bit confusing, and I'm not sure, where to actually introduce it now.
Could QuIC take a look please?
Thx. ^^
best regards
leviathan
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
@ 2010-07-16 7:33 David Lanzendörfer
0 siblings, 0 replies; 10+ messages in thread
From: David Lanzendörfer @ 2010-07-16 7:33 UTC (permalink / raw)
To: linux-arm-msm
[-- Attachment #1: Type: text/plain, Size: 160 bytes --]
>What do you mean by iface usb0? Are you referring to usb0 network interface
>showing up in ifconfig?
exactly
>Thanks,
>Pavan
best regards
leviathan
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Adding usb gadget support
2010-07-16 7:33 [PATCH] Adding usb gadget support David Lanzendörfer
@ 2010-07-16 17:14 ` Michael Bohan
0 siblings, 0 replies; 10+ messages in thread
From: Michael Bohan @ 2010-07-16 17:14 UTC (permalink / raw)
To: David Lanzendörfer; +Cc: linux-arm-msm
On 7/16/2010 12:33 AM, David Lanzendörfer wrote:
> Also here we are using phy_reset
> https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=blob;f=drivers/usb/gadget/msm72k_udc.c;h=f3669507bcd929b043e573d08e600171cc0c87de;hb=refs/heads/aosp/android-msm-2.6.32
>
> Its needed for cable plug/unplug event recognition as it looks like.
> So we realy seem to need phy_reset.
> In the android branch phy_reset() was declared inside a second devices.c file called
> devices_htc.c
> I considered it a bit confusing, and I'm not sure, where to actually introduce it now.
> Could QuIC take a look please?
Look at the file you mentioned for an example of how to connect the phy
reset function:
https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=blob;f=arch/arm/mach-msm/devices_htc.c;h=e5f65e720656db1e0349e2d981e50f514eac8f2f;hb=refs/heads/aosp/android-msm-2.6.32
You just need to:
1. Make an instance of the msm_hsusb_platform_data.
2. Assign the 'phy_reset' pointer to your added function.
3. Assign that msm_device_hsusb platform_data to your pdata struct.
4. Register the platform device.
Michael
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2010-07-16 17:14 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-16 7:33 [PATCH] Adding usb gadget support David Lanzendörfer
2010-07-16 17:14 ` Michael Bohan
-- strict thread matches above, loose matches on Subject: below --
2010-07-16 7:33 David Lanzendörfer
2010-07-15 17:31 David Lanzendörfer
2010-07-15 17:49 ` Michael Bohan
2010-07-15 19:05 ` David Lanzendörfer
2010-07-15 21:46 ` David Lanzendörfer
2010-07-15 22:34 ` Michael Bohan
2010-07-15 23:08 ` Tim HRM
2010-07-16 6:07 ` Pavan Kondeti
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).