From 6acc1bcd91b4f7e5c9fc36a808e47b0e2f4ff7bf Mon Sep 17 00:00:00 2001 From: Yu Wang Date: Fri, 30 Mar 2012 16:08:31 +0800 Subject: [PATCH] dwc_otg: Provide sysfs interface to user space to switch host mode and device mode. BZ: 29834 Because currently there is no workable micro A usb3 cable on market. So provide sysfs interface to support switch host mode and device mode from user space. Usage(commands): switch to host: echo A > /sys/devices/pci0000:00/0000:00:11:0/otg_id switch to device: echo A > /sys/devices/pci0000:00/0000:00:11:0/otg_id Signed-off-by: Yu Wang Change-Id: I60a4cefa72508f6f3dc70abbea5bc8f954eb644b Signed-off-by: Yu Wang Reviewed-on: http://android.intel.com:8080/41865 Reviewed-by: Meng, Zhe Tested-by: Meng, Zhe Reviewed-by: Tang, Richard Reviewed-by: buildbot Tested-by: buildbot --- arch/x86/configs/i386_mrfl_defconfig | 102 +++++++++++++++++++++++++++++- drivers/usb/otg/dwc_otg3.c | 116 +++++++++++++++++++++++++++++++---- include/linux/usb/dwc_otg3.h | 3 + 3 files changed, 207 insertions(+), 14 deletions(-) diff --git a/arch/x86/configs/i386_mrfl_defconfig b/arch/x86/configs/i386_mrfl_defconfig index 910fc4e..ead2109 100644 --- a/arch/x86/configs/i386_mrfl_defconfig +++ b/arch/x86/configs/i386_mrfl_defconfig @@ -1798,7 +1798,107 @@ CONFIG_LOGO_LINUX_VGA16=y CONFIG_LOGO_LINUX_CLUT224=y # CONFIG_SOUND is not set # CONFIG_HID_SUPPORT is not set -# CONFIG_USB_SUPPORT is not set +CONFIG_USB_SUPPORT=y +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +CONFIG_USB_OTG=y +CONFIG_USB_OTG_WHITELIST=y +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_WHCI_HCD is not set +# CONFIG_USB_HWA_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_LANGWELL_OTG is not set +# CONFIG_USB_PENWELL_OTG is not set +# CONFIG_USB_PENWELL_OTG_TEST is not set +CONFIG_USB_DWC_OTG_XCEIV=y # CONFIG_UWB is not set CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set diff --git a/drivers/usb/otg/dwc_otg3.c b/drivers/usb/otg/dwc_otg3.c index d7921b5..47afcd3 100644 --- a/drivers/usb/otg/dwc_otg3.c +++ b/drivers/usb/otg/dwc_otg3.c @@ -14,7 +14,9 @@ #include #include +#define SUPPORT_USER_ID_CHANGE_EVENTS +static int otg_id = -1; static struct mutex lock; static const char driver_name[] = "dwc_otg3"; static void __devexit dwc_otg_remove(struct pci_dev *pdev); @@ -431,13 +433,11 @@ static enum dwc_otg_state do_charger_detection(struct dwc_otg2 *otg) static enum dwc_otg_state do_connector_id_status(struct dwc_otg2 *otg) { - u32 events = 0; + u32 events = 0, user_events = 0; enum dwc_otg_state state = DWC_STATE_INVALID; - u32 osts = 0; otg_dbg(otg, "\n"); - /* Reset ADP related registers */ otg_write(otg, ADPCFG, 0); otg_write(otg, ADPCTL, 0); @@ -451,6 +451,7 @@ static enum dwc_otg_state do_connector_id_status(struct dwc_otg2 *otg) msleep(60); +#ifndef SUPPORT_USER_ID_CHANGE_EVENTS osts = otg_read(otg, OSTS); if (!(osts & OSTS_CONN_ID_STS)) { otg_dbg(otg, "Connector ID is A\n"); @@ -462,11 +463,14 @@ static enum dwc_otg_state do_connector_id_status(struct dwc_otg2 *otg) else otg_dbg(otg, "B session invalied, so haven't connect HOST\n"); } +#endif sleep_until_event(otg, OEVT_CONN_ID_STS_CHNG_EVNT \ | OEVT_B_DEV_VBUS_CHNG_EVNT \ | OEVT_B_DEV_SES_VLD_DET_EVNT, \ - 0, 0, &events, NULL, NULL, 0); + 0, USER_ID_B_CHANGE_EVENT \ + | USER_ID_A_CHANGE_EVENT, &events,\ + NULL, &user_events, 0); if (events & (OEVT_B_DEV_VBUS_CHNG_EVNT | \ OEVT_B_DEV_SES_VLD_DET_EVNT)) { @@ -479,6 +483,17 @@ static enum dwc_otg_state do_connector_id_status(struct dwc_otg2 *otg) state = DWC_STATE_A_HOST; } +#ifdef SUPPORT_USER_ID_CHANGE_EVENTS + if (user_events & USER_ID_A_CHANGE_EVENT) { + otg_dbg(otg, "events is user id A change\n"); + state = DWC_STATE_A_HOST; + } + + if (user_events & USER_ID_B_CHANGE_EVENT) { + otg_dbg(otg, "events is user id B change\n"); + state = DWC_STATE_B_PERIPHERAL; + } +#endif return state; } @@ -505,6 +520,7 @@ static enum dwc_otg_state do_a_host(struct dwc_otg2 *otg) ret = start_host(otg); otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT; + user_mask = USER_ID_B_CHANGE_EVENT; rc = sleep_until_event(otg, otg_mask, 0, user_mask, @@ -536,7 +552,8 @@ static int do_b_peripheral(struct dwc_otg2 *otg) otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT \ | OEVT_B_DEV_VBUS_CHNG_EVNT \ | OEVT_B_DEV_SES_VLD_DET_EVNT; - user_mask = 0; + + user_mask = USER_ID_A_CHANGE_EVENT; rc = sleep_until_event(otg, otg_mask, 0, user_mask, @@ -551,12 +568,12 @@ static int do_b_peripheral(struct dwc_otg2 *otg) if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) { otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT!\n"); - state = DWC_STATE_CHARGER_DETECTION; + return DWC_STATE_CHARGER_DETECTION; } if (otg_events & OEVT_B_DEV_SES_VLD_DET_EVNT) { otg_dbg(otg, "OEVT_B_DEV_SES_VLD_DET_EVNT!\n"); - state = DWC_STATE_CHARGER_DETECTION; + return DWC_STATE_CHARGER_DETECTION; } return DWC_STATE_INVALID; @@ -584,12 +601,14 @@ int otg_main_thread(void *data) set_freezable(); reset_hw(otg); +#ifndef SUPPORT_USER_ID_CHANGE_EVENTS if (request_irq(otg->irqnum, dwc_otg_irq, IRQF_SHARED, driver_name, otg) != 0) { otg_dbg(otg, "request interrupt %d failed\n", otg->irqnum); return -EINVAL; } +#endif otg_dbg(otg, "Thread running\n"); while (otg->state != DWC_STATE_TERMINATED) { @@ -643,7 +662,7 @@ int otg_main_thread(void *data) static void start_main_thread(struct dwc_otg2 *otg) { mutex_lock(&lock); - if (!otg->main_thread && otg->otg.gadget && otg->otg.host) { + if (!otg->main_thread /* && otg->otg.gadget */&& otg->otg.host) { otg_dbg(otg, "Starting OTG main thread\n"); otg->main_thread = kthread_create(otg_main_thread, otg, "otg"); wake_up_process(otg->main_thread); @@ -766,7 +785,7 @@ static int dwc_otg2_set_peripheral(struct otg_transceiver *x, struct dwc_otg2 *otg; if (!x) { - otg_err(otg, "%s, otg is NULL!\n", __func__); + otg_err(otg, "otg is NULL!\n"); return -ENODEV; } @@ -790,7 +809,7 @@ static int dwc_otg2_set_host(struct otg_transceiver *x, struct usb_bus *host) struct dwc_otg2 *otg; if (!x) { - otg_err(otg, "%s, otg is NULL!\n", __func__); + otg_err(otg, "otg is NULL!\n"); return -ENODEV; } @@ -813,7 +832,7 @@ static int dwc_otg2_received_host_release(struct otg_transceiver *x) unsigned long flags; struct dwc_otg2 *otg; if (!x) { - otg_err(otg, "%s otg is NULL!\n", __func__); + otg_err(otg, "otg is NULL!\n"); return -ENODEV; } @@ -830,7 +849,70 @@ static int dwc_otg2_received_host_release(struct otg_transceiver *x) return 0; } -static struct dwc_otg2 *the_transceiver; +#ifdef SUPPORT_USER_ID_CHANGE_EVENTS +static ssize_t store_otg_id(struct device *_dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dwc_otg2 *otg = the_transceiver; + unsigned long flags; + + if (count != 2) { + otg_err(otg, "return EINVAL\n"); + return -EINVAL; + } + + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; + + switch (buf[0]) { + case 'a': + case 'A': + otg_dbg(otg, "Change ID to A\n"); + otg->user_events |= USER_ID_A_CHANGE_EVENT; + spin_lock_irqsave(&otg->lock, flags); + wakeup_main_thread(otg); + otg_id = 0; + spin_unlock_irqrestore(&otg->lock, flags); + return count; + case 'b': + case 'B': + otg_dbg(otg, "Change ID to B\n"); + otg->user_events |= USER_ID_B_CHANGE_EVENT; + spin_lock_irqsave(&otg->lock, flags); + wakeup_main_thread(otg); + otg_id = 1; + spin_unlock_irqrestore(&otg->lock, flags); + return count; + default: + otg_err(otg, "Just support change ID to A!\n"); + return -EINVAL; + } + + return count; +} + +static ssize_t +show_otg_id(struct device *_dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, + "USB OTG ID: %s\n", + (otg_id ? "B" : "A") + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(otg_id, S_IRUGO|S_IWUSR|S_IWGRP,\ + show_otg_id, store_otg_id); +#endif + static int dwc_otg_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -839,7 +921,6 @@ static int dwc_otg_probe(struct pci_dev *pdev, struct platform_device *dwc_host, *dwc_gadget; unsigned long resource, len; int retval; - otg_dbg(otg, "%s is calling\n", __func__); if (pci_enable_device(pdev) < 0) { otg_err(otg, "pci device enable failed\n"); @@ -964,6 +1045,15 @@ static int dwc_otg_probe(struct pci_dev *pdev, otg->gadget = dwc_gadget; otg->irqnum = pdev->irq; +#ifdef SUPPORT_USER_ID_CHANGE_EVENTS + retval = device_create_file(&pdev->dev, &dev_attr_otg_id); + if (retval < 0) { + otg_dbg(otg, + "Can't register sysfs attribute: %d\n", retval); + goto exit; + } +#endif + return 0; exit: if (the_transceiver) diff --git a/include/linux/usb/dwc_otg3.h b/include/linux/usb/dwc_otg3.h index 42121d9..9ade558 100644 --- a/include/linux/usb/dwc_otg3.h +++ b/include/linux/usb/dwc_otg3.h @@ -320,6 +320,9 @@ struct dwc_otg2 { #define USER_RSP_EVENT 0x10 /** Host release event */ #define PCD_RECEIVED_HOST_RELEASE_EVENT 0x20 + /** User space ID switch event */ +#define USER_ID_A_CHANGE_EVENT 0x40 +#define USER_ID_B_CHANGE_EVENT 0x80 /* States */ enum dwc_otg_state prev; -- 2.7.4