summaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r--drivers/usb/otg/Kconfig48
-rw-r--r--drivers/usb/otg/Makefile2
-rw-r--r--drivers/usb/otg/ab8500-usb.c2
-rw-r--r--drivers/usb/otg/fsl_otg.c15
-rw-r--r--drivers/usb/otg/langwell_otg.c2347
-rw-r--r--drivers/usb/otg/mv_otg.c959
-rw-r--r--drivers/usb/otg/mv_otg.h165
7 files changed, 1151 insertions, 2387 deletions
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index c66481ad98d..735ef4c2339 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -82,23 +82,9 @@ config NOP_USB_XCEIV
tristate "NOP USB Transceiver Driver"
select USB_OTG_UTILS
help
- this driver is to be used by all the usb transceiver which are either
- built-in with usb ip or which are autonomous and doesn't require any
- phy programming such as ISP1x04 etc.
-
-config USB_LANGWELL_OTG
- tristate "Intel Langwell USB OTG dual-role support"
- depends on USB && PCI && INTEL_SCU_IPC
- select USB_OTG
- select USB_OTG_UTILS
- help
- Say Y here if you want to build Intel Langwell USB OTG
- transciever driver in kernel. This driver implements role
- switch between EHCI host driver and Langwell USB OTG
- client driver.
-
- To compile this driver as a module, choose M here: the
- module will be called langwell_otg.
+ This driver is to be used by all the usb transceiver which are either
+ built-in with usb ip or which are autonomous and doesn't require any
+ phy programming such as ISP1x04 etc.
config USB_MSM_OTG
tristate "OTG support for Qualcomm on-chip USB controller"
@@ -114,20 +100,32 @@ config USB_MSM_OTG
has an external PHY.
config AB8500_USB
- tristate "AB8500 USB Transceiver Driver"
- depends on AB8500_CORE
- select USB_OTG_UTILS
- help
- Enable this to support the USB OTG transceiver in AB8500 chip.
- This transceiver supports high and full speed devices plus,
- in host mode, low speed.
+ tristate "AB8500 USB Transceiver Driver"
+ depends on AB8500_CORE
+ select USB_OTG_UTILS
+ help
+ Enable this to support the USB OTG transceiver in AB8500 chip.
+ This transceiver supports high and full speed devices plus,
+ in host mode, low speed.
config FSL_USB2_OTG
bool "Freescale USB OTG Transceiver Driver"
- depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2
+ depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND
select USB_OTG
select USB_OTG_UTILS
help
Enable this to support Freescale USB OTG transceiver.
+config USB_MV_OTG
+ tristate "Marvell USB OTG support"
+ depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND
+ select USB_OTG
+ select USB_OTG_UTILS
+ help
+ Say Y here if you want to build Marvell USB OTG transciever
+ driver in kernel (including PXA and MMP series). This driver
+ implements role switch between EHCI host driver and gadget driver.
+
+ To compile this driver as a module, choose M here.
+
endif # USB || OTG
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 566655c5333..41aa5098b13 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o
-obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
obj-$(CONFIG_USB_ULPI) += ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o
@@ -21,3 +20,4 @@ obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o
obj-$(CONFIG_AB8500_USB) += ab8500-usb.o
fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o
obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o
+obj-$(CONFIG_USB_MV_OTG) += mv_otg.o
diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c
index 07ccea9ada4..74fe6e62e0f 100644
--- a/drivers/usb/otg/ab8500-usb.c
+++ b/drivers/usb/otg/ab8500-usb.c
@@ -30,7 +30,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
#define AB8500_MAIN_WD_CTRL_REG 0x01
#define AB8500_USB_LINE_STAT_REG 0x80
diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c
index 0f420b25e9a..a190850d2d3 100644
--- a/drivers/usb/otg/fsl_otg.c
+++ b/drivers/usb/otg/fsl_otg.c
@@ -639,7 +639,7 @@ static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA)
* Delayed pin detect interrupt processing.
*
* When the Mini-A cable is disconnected from the board,
- * the pin-detect interrupt happens before the disconnnect
+ * the pin-detect interrupt happens before the disconnect
* interrupts for the connected device(s). In order to
* process the disconnect interrupt(s) prior to switching
* roles, the pin-detect interrupts are delayed, and handled
@@ -1151,18 +1151,7 @@ struct platform_driver fsl_otg_driver = {
},
};
-static int __init fsl_usb_otg_init(void)
-{
- pr_info(DRIVER_INFO "\n");
- return platform_driver_register(&fsl_otg_driver);
-}
-module_init(fsl_usb_otg_init);
-
-static void __exit fsl_usb_otg_exit(void)
-{
- platform_driver_unregister(&fsl_otg_driver);
-}
-module_exit(fsl_usb_otg_exit);
+module_platform_driver(fsl_otg_driver);
MODULE_DESCRIPTION(DRIVER_INFO);
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c
deleted file mode 100644
index f08f784086f..00000000000
--- a/drivers/usb/otg/langwell_otg.c
+++ /dev/null
@@ -1,2347 +0,0 @@
-/*
- * Intel Langwell USB OTG transceiver driver
- * Copyright (C) 2008 - 2010, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-/* This driver helps to switch Langwell OTG controller function between host
- * and peripheral. It works with EHCI driver and Langwell client controller
- * driver together.
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/moduleparam.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/hcd.h>
-#include <linux/notifier.h>
-#include <linux/delay.h>
-#include <asm/intel_scu_ipc.h>
-
-#include <linux/usb/langwell_otg.h>
-
-#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver"
-#define DRIVER_VERSION "July 10, 2010"
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>");
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-static const char driver_name[] = "langwell_otg";
-
-static int langwell_otg_probe(struct pci_dev *pdev,
- const struct pci_device_id *id);
-static void langwell_otg_remove(struct pci_dev *pdev);
-static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message);
-static int langwell_otg_resume(struct pci_dev *pdev);
-
-static int langwell_otg_set_host(struct otg_transceiver *otg,
- struct usb_bus *host);
-static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
- struct usb_gadget *gadget);
-static int langwell_otg_start_srp(struct otg_transceiver *otg);
-
-static const struct pci_device_id pci_ids[] = {{
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
- .vendor = 0x8086,
- .device = 0x0811,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-}, { /* end: all zeroes */ }
-};
-
-static struct pci_driver otg_pci_driver = {
- .name = (char *) driver_name,
- .id_table = pci_ids,
-
- .probe = langwell_otg_probe,
- .remove = langwell_otg_remove,
-
- .suspend = langwell_otg_suspend,
- .resume = langwell_otg_resume,
-};
-
-/* HSM timers */
-static inline struct langwell_otg_timer *otg_timer_initializer
-(void (*function)(unsigned long), unsigned long expires, unsigned long data)
-{
- struct langwell_otg_timer *timer;
- timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL);
- if (timer == NULL)
- return timer;
-
- timer->function = function;
- timer->expires = expires;
- timer->data = data;
- return timer;
-}
-
-static struct langwell_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr,
- *b_se0_srp_tmr, *b_srp_init_tmr;
-
-static struct list_head active_timers;
-
-static struct langwell_otg *the_transceiver;
-
-/* host/client notify transceiver when event affects HNP state */
-void langwell_update_transceiver(void)
-{
- struct langwell_otg *lnw = the_transceiver;
-
- dev_dbg(lnw->dev, "transceiver is updated\n");
-
- if (!lnw->qwork)
- return ;
-
- queue_work(lnw->qwork, &lnw->work);
-}
-EXPORT_SYMBOL(langwell_update_transceiver);
-
-static int langwell_otg_set_host(struct otg_transceiver *otg,
- struct usb_bus *host)
-{
- otg->host = host;
-
- return 0;
-}
-
-static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
- struct usb_gadget *gadget)
-{
- otg->gadget = gadget;
-
- return 0;
-}
-
-static int langwell_otg_set_power(struct otg_transceiver *otg,
- unsigned mA)
-{
- return 0;
-}
-
-/* A-device drives vbus, controlled through IPC commands */
-static int langwell_otg_set_vbus(struct otg_transceiver *otg, bool enabled)
-{
- struct langwell_otg *lnw = the_transceiver;
- u8 sub_id;
-
- dev_dbg(lnw->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off");
-
- if (enabled)
- sub_id = 0x8; /* Turn on the VBus */
- else
- sub_id = 0x9; /* Turn off the VBus */
-
- if (intel_scu_ipc_simple_command(0xef, sub_id)) {
- dev_dbg(lnw->dev, "Failed to set Vbus via IPC commands\n");
- return -EBUSY;
- }
-
- dev_dbg(lnw->dev, "%s --->\n", __func__);
-
- return 0;
-}
-
-/* charge vbus or discharge vbus through a resistor to ground */
-static void langwell_otg_chrg_vbus(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- u32 val;
-
- val = readl(lnw->iotg.base + CI_OTGSC);
-
- if (on)
- writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC,
- lnw->iotg.base + CI_OTGSC);
- else
- writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD,
- lnw->iotg.base + CI_OTGSC);
-}
-
-/* Start SRP */
-static int langwell_otg_start_srp(struct otg_transceiver *otg)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 val;
-
- dev_dbg(lnw->dev, "%s --->\n", __func__);
-
- val = readl(iotg->base + CI_OTGSC);
-
- writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP,
- iotg->base + CI_OTGSC);
-
- /* Check if the data plus is finished or not */
- msleep(8);
- val = readl(iotg->base + CI_OTGSC);
- if (val & (OTGSC_HADP | OTGSC_DP))
- dev_dbg(lnw->dev, "DataLine SRP Error\n");
-
- /* Disable interrupt - b_sess_vld */
- val = readl(iotg->base + CI_OTGSC);
- val &= (~(OTGSC_BSVIE | OTGSC_BSEIE));
- writel(val, iotg->base + CI_OTGSC);
-
- /* Start VBus SRP, drive vbus to generate VBus pulse */
- iotg->otg.set_vbus(&iotg->otg, true);
- msleep(15);
- iotg->otg.set_vbus(&iotg->otg, false);
-
- /* Enable interrupt - b_sess_vld*/
- val = readl(iotg->base + CI_OTGSC);
- dev_dbg(lnw->dev, "after VBUS pulse otgsc = %x\n", val);
-
- val |= (OTGSC_BSVIE | OTGSC_BSEIE);
- writel(val, iotg->base + CI_OTGSC);
-
- /* If Vbus is valid, then update the hsm */
- if (val & OTGSC_BSV) {
- dev_dbg(lnw->dev, "no b_sess_vld interrupt\n");
-
- lnw->iotg.hsm.b_sess_vld = 1;
- langwell_update_transceiver();
- }
-
- dev_dbg(lnw->dev, "%s <---\n", __func__);
- return 0;
-}
-
-/* stop SOF via bus_suspend */
-static void langwell_otg_loc_sof(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct usb_hcd *hcd;
- int err;
-
- dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "suspend" : "resume");
-
- hcd = bus_to_hcd(lnw->iotg.otg.host);
- if (on)
- err = hcd->driver->bus_resume(hcd);
- else
- err = hcd->driver->bus_suspend(hcd);
-
- if (err)
- dev_dbg(lnw->dev, "Fail to resume/suspend USB bus - %d\n", err);
-
- dev_dbg(lnw->dev, "%s <---\n", __func__);
-}
-
-static int langwell_otg_check_otgsc(void)
-{
- struct langwell_otg *lnw = the_transceiver;
- u32 otgsc, usbcfg;
-
- dev_dbg(lnw->dev, "check sync OTGSC and USBCFG registers\n");
-
- otgsc = readl(lnw->iotg.base + CI_OTGSC);
- usbcfg = readl(lnw->usbcfg);
-
- dev_dbg(lnw->dev, "OTGSC = %08x, USBCFG = %08x\n",
- otgsc, usbcfg);
- dev_dbg(lnw->dev, "OTGSC_AVV = %d\n", !!(otgsc & OTGSC_AVV));
- dev_dbg(lnw->dev, "USBCFG.VBUSVAL = %d\n",
- !!(usbcfg & USBCFG_VBUSVAL));
- dev_dbg(lnw->dev, "OTGSC_ASV = %d\n", !!(otgsc & OTGSC_ASV));
- dev_dbg(lnw->dev, "USBCFG.AVALID = %d\n",
- !!(usbcfg & USBCFG_AVALID));
- dev_dbg(lnw->dev, "OTGSC_BSV = %d\n", !!(otgsc & OTGSC_BSV));
- dev_dbg(lnw->dev, "USBCFG.BVALID = %d\n",
- !!(usbcfg & USBCFG_BVALID));
- dev_dbg(lnw->dev, "OTGSC_BSE = %d\n", !!(otgsc & OTGSC_BSE));
- dev_dbg(lnw->dev, "USBCFG.SESEND = %d\n",
- !!(usbcfg & USBCFG_SESEND));
-
- /* Check USBCFG VBusValid/AValid/BValid/SessEnd */
- if (!!(otgsc & OTGSC_AVV) ^ !!(usbcfg & USBCFG_VBUSVAL)) {
- dev_dbg(lnw->dev, "OTGSC.AVV != USBCFG.VBUSVAL\n");
- goto err;
- }
- if (!!(otgsc & OTGSC_ASV) ^ !!(usbcfg & USBCFG_AVALID)) {
- dev_dbg(lnw->dev, "OTGSC.ASV != USBCFG.AVALID\n");
- goto err;
- }
- if (!!(otgsc & OTGSC_BSV) ^ !!(usbcfg & USBCFG_BVALID)) {
- dev_dbg(lnw->dev, "OTGSC.BSV != USBCFG.BVALID\n");
- goto err;
- }
- if (!!(otgsc & OTGSC_BSE) ^ !!(usbcfg & USBCFG_SESEND)) {
- dev_dbg(lnw->dev, "OTGSC.BSE != USBCFG.SESSEN\n");
- goto err;
- }
-
- dev_dbg(lnw->dev, "OTGSC and USBCFG are synced\n");
-
- return 0;
-
-err:
- dev_warn(lnw->dev, "OTGSC isn't equal to USBCFG\n");
- return -EPIPE;
-}
-
-
-static void langwell_otg_phy_low_power(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u8 val, phcd;
- int retval;
-
- dev_dbg(lnw->dev, "%s ---> %s mode\n",
- __func__, on ? "Low power" : "Normal");
-
- phcd = 0x40;
-
- val = readb(iotg->base + CI_HOSTPC1 + 2);
-
- if (on) {
- /* Due to hardware issue, after set PHCD, sync will failed
- * between USBCFG and OTGSC, so before set PHCD, check if
- * sync is in process now. If the answer is "yes", then do
- * not touch PHCD bit */
- retval = langwell_otg_check_otgsc();
- if (retval) {
- dev_dbg(lnw->dev, "Skip PHCD programming..\n");
- return ;
- }
-
- writeb(val | phcd, iotg->base + CI_HOSTPC1 + 2);
- } else
- writeb(val & ~phcd, iotg->base + CI_HOSTPC1 + 2);
-
- dev_dbg(lnw->dev, "%s <--- done\n", __func__);
-}
-
-/* After drv vbus, add 5 ms delay to set PHCD */
-static void langwell_otg_phy_low_power_wait(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
-
- dev_dbg(lnw->dev, "add 5ms delay before programing PHCD\n");
-
- mdelay(5);
- langwell_otg_phy_low_power(on);
-}
-
-/* Enable/Disable OTG interrupt */
-static void langwell_otg_intr(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 val;
-
- dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off");
-
- val = readl(iotg->base + CI_OTGSC);
-
- /* OTGSC_INT_MASK doesn't contains 1msInt */
- if (on) {
- val = val | (OTGSC_INT_MASK);
- writel(val, iotg->base + CI_OTGSC);
- } else {
- val = val & ~(OTGSC_INT_MASK);
- writel(val, iotg->base + CI_OTGSC);
- }
-
- dev_dbg(lnw->dev, "%s <---\n", __func__);
-}
-
-/* set HAAR: Hardware Assist Auto-Reset */
-static void langwell_otg_HAAR(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 val;
-
- dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off");
-
- val = readl(iotg->base + CI_OTGSC);
- if (on)
- writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR,
- iotg->base + CI_OTGSC);
- else
- writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR,
- iotg->base + CI_OTGSC);
-
- dev_dbg(lnw->dev, "%s <---\n", __func__);
-}
-
-/* set HABA: Hardware Assist B-Disconnect to A-Connect */
-static void langwell_otg_HABA(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 val;
-
- dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off");
-
- val = readl(iotg->base + CI_OTGSC);
- if (on)
- writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA,
- iotg->base + CI_OTGSC);
- else
- writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA,
- iotg->base + CI_OTGSC);
-
- dev_dbg(lnw->dev, "%s <---\n", __func__);
-}
-
-static int langwell_otg_check_se0_srp(int on)
-{
- struct langwell_otg *lnw = the_transceiver;
- int delay_time = TB_SE0_SRP * 10;
- u32 val;
-
- dev_dbg(lnw->dev, "%s --->\n", __func__);
-
- do {
- udelay(100);
- if (!delay_time--)
- break;
- val = readl(lnw->iotg.base + CI_PORTSC1);
- val &= PORTSC_LS;
- } while (!val);
-
- dev_dbg(lnw->dev, "%s <---\n", __func__);
- return val;
-}
-
-/* The timeout callback function to set time out bit */
-static void set_tmout(unsigned long indicator)
-{
- *(int *)indicator = 1;
-}
-
-void langwell_otg_nsf_msg(unsigned long indicator)
-{
- struct langwell_otg *lnw = the_transceiver;
-
- switch (indicator) {
- case 2:
- case 4:
- case 6:
- case 7:
- dev_warn(lnw->dev,
- "OTG:NSF-%lu - deivce not responding\n", indicator);
- break;
- case 3:
- dev_warn(lnw->dev,
- "OTG:NSF-%lu - deivce not supported\n", indicator);
- break;
- default:
- dev_warn(lnw->dev, "Do not have this kind of NSF\n");
- break;
- }
-}
-
-/* Initialize timers */
-static int langwell_otg_init_timers(struct otg_hsm *hsm)
-{
- /* HSM used timers */
- a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
- (unsigned long)&hsm->a_wait_vrise_tmout);
- if (a_wait_vrise_tmr == NULL)
- return -ENOMEM;
- a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
- (unsigned long)&hsm->a_aidl_bdis_tmout);
- if (a_aidl_bdis_tmr == NULL)
- return -ENOMEM;
- b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
- (unsigned long)&hsm->b_se0_srp);
- if (b_se0_srp_tmr == NULL)
- return -ENOMEM;
- b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT,
- (unsigned long)&hsm->b_srp_init_tmout);
- if (b_srp_init_tmr == NULL)
- return -ENOMEM;
-
- return 0;
-}
-
-/* Free timers */
-static void langwell_otg_free_timers(void)
-{
- kfree(a_wait_vrise_tmr);
- kfree(a_aidl_bdis_tmr);
- kfree(b_se0_srp_tmr);
- kfree(b_srp_init_tmr);
-}
-
-/* The timeout callback function to set time out bit */
-static void langwell_otg_timer_fn(unsigned long indicator)
-{
- struct langwell_otg *lnw = the_transceiver;
-
- *(int *)indicator = 1;
-
- dev_dbg(lnw->dev, "kernel timer - timeout\n");
-
- langwell_update_transceiver();
-}
-
-/* kernel timer used instead of HW based interrupt */
-static void langwell_otg_add_ktimer(enum langwell_otg_timer_type timers)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- unsigned long j = jiffies;
- unsigned long data, time;
-
- switch (timers) {
- case TA_WAIT_VRISE_TMR:
- iotg->hsm.a_wait_vrise_tmout = 0;
- data = (unsigned long)&iotg->hsm.a_wait_vrise_tmout;
- time = TA_WAIT_VRISE;
- break;
- case TA_WAIT_BCON_TMR:
- iotg->hsm.a_wait_bcon_tmout = 0;
- data = (unsigned long)&iotg->hsm.a_wait_bcon_tmout;
- time = TA_WAIT_BCON;
- break;
- case TA_AIDL_BDIS_TMR:
- iotg->hsm.a_aidl_bdis_tmout = 0;
- data = (unsigned long)&iotg->hsm.a_aidl_bdis_tmout;
- time = TA_AIDL_BDIS;
- break;
- case TB_ASE0_BRST_TMR:
- iotg->hsm.b_ase0_brst_tmout = 0;
- data = (unsigned long)&iotg->hsm.b_ase0_brst_tmout;
- time = TB_ASE0_BRST;
- break;
- case TB_SRP_INIT_TMR:
- iotg->hsm.b_srp_init_tmout = 0;
- data = (unsigned long)&iotg->hsm.b_srp_init_tmout;
- time = TB_SRP_INIT;
- break;
- case TB_SRP_FAIL_TMR:
- iotg->hsm.b_srp_fail_tmout = 0;
- data = (unsigned long)&iotg->hsm.b_srp_fail_tmout;
- time = TB_SRP_FAIL;
- break;
- case TB_BUS_SUSPEND_TMR:
- iotg->hsm.b_bus_suspend_tmout = 0;
- data = (unsigned long)&iotg->hsm.b_bus_suspend_tmout;
- time = TB_BUS_SUSPEND;
- break;
- default:
- dev_dbg(lnw->dev, "unknown timer, cannot enable it\n");
- return;
- }
-
- lnw->hsm_timer.data = data;
- lnw->hsm_timer.function = langwell_otg_timer_fn;
- lnw->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */
-
- add_timer(&lnw->hsm_timer);
-
- dev_dbg(lnw->dev, "add timer successfully\n");
-}
-
-/* Add timer to timer list */
-static void langwell_otg_add_timer(void *gtimer)
-{
- struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
- struct langwell_otg_timer *tmp_timer;
- struct intel_mid_otg_xceiv *iotg = &the_transceiver->iotg;
- u32 val32;
-
- /* Check if the timer is already in the active list,
- * if so update timer count
- */
- list_for_each_entry(tmp_timer, &active_timers, list)
- if (tmp_timer == timer) {
- timer->count = timer->expires;
- return;
- }
- timer->count = timer->expires;
-
- if (list_empty(&active_timers)) {
- val32 = readl(iotg->base + CI_OTGSC);
- writel(val32 | OTGSC_1MSE, iotg->base + CI_OTGSC);
- }
-
- list_add_tail(&timer->list, &active_timers);
-}
-
-/* Remove timer from the timer list; clear timeout status */
-static void langwell_otg_del_timer(void *gtimer)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
- struct langwell_otg_timer *tmp_timer, *del_tmp;
- u32 val32;
-
- list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
- if (tmp_timer == timer)
- list_del(&timer->list);
-
- if (list_empty(&active_timers)) {
- val32 = readl(lnw->iotg.base + CI_OTGSC);
- writel(val32 & ~OTGSC_1MSE, lnw->iotg.base + CI_OTGSC);
- }
-}
-
-/* Reduce timer count by 1, and find timeout conditions.*/
-static int langwell_otg_tick_timer(u32 *int_sts)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct langwell_otg_timer *tmp_timer, *del_tmp;
- int expired = 0;
-
- list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
- tmp_timer->count--;
- /* check if timer expires */
- if (!tmp_timer->count) {
- list_del(&tmp_timer->list);
- tmp_timer->function(tmp_timer->data);
- expired = 1;
- }
- }
-
- if (list_empty(&active_timers)) {
- dev_dbg(lnw->dev, "tick timer: disable 1ms int\n");
- *int_sts = *int_sts & ~OTGSC_1MSE;
- }
- return expired;
-}
-
-static void reset_otg(void)
-{
- struct langwell_otg *lnw = the_transceiver;
- int delay_time = 1000;
- u32 val;
-
- dev_dbg(lnw->dev, "reseting OTG controller ...\n");
- val = readl(lnw->iotg.base + CI_USBCMD);
- writel(val | USBCMD_RST, lnw->iotg.base + CI_USBCMD);
- do {
- udelay(100);
- if (!delay_time--)
- dev_dbg(lnw->dev, "reset timeout\n");
- val = readl(lnw->iotg.base + CI_USBCMD);
- val &= USBCMD_RST;
- } while (val != 0);
- dev_dbg(lnw->dev, "reset done.\n");
-}
-
-static void set_host_mode(void)
-{
- struct langwell_otg *lnw = the_transceiver;
- u32 val;
-
- reset_otg();
- val = readl(lnw->iotg.base + CI_USBMODE);
- val = (val & (~USBMODE_CM)) | USBMODE_HOST;
- writel(val, lnw->iotg.base + CI_USBMODE);
-}
-
-static void set_client_mode(void)
-{
- struct langwell_otg *lnw = the_transceiver;
- u32 val;
-
- reset_otg();
- val = readl(lnw->iotg.base + CI_USBMODE);
- val = (val & (~USBMODE_CM)) | USBMODE_DEVICE;
- writel(val, lnw->iotg.base + CI_USBMODE);
-}
-
-static void init_hsm(void)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 val32;
-
- /* read OTGSC after reset */
- val32 = readl(lnw->iotg.base + CI_OTGSC);
- dev_dbg(lnw->dev, "%s: OTGSC init value = 0x%x\n", __func__, val32);
-
- /* set init state */
- if (val32 & OTGSC_ID) {
- iotg->hsm.id = 1;
- iotg->otg.default_a = 0;
- set_client_mode();
- iotg->otg.state = OTG_STATE_B_IDLE;
- } else {
- iotg->hsm.id = 0;
- iotg->otg.default_a = 1;
- set_host_mode();
- iotg->otg.state = OTG_STATE_A_IDLE;
- }
-
- /* set session indicator */
- if (val32 & OTGSC_BSE)
- iotg->hsm.b_sess_end = 1;
- if (val32 & OTGSC_BSV)
- iotg->hsm.b_sess_vld = 1;
- if (val32 & OTGSC_ASV)
- iotg->hsm.a_sess_vld = 1;
- if (val32 & OTGSC_AVV)
- iotg->hsm.a_vbus_vld = 1;
-
- /* defautly power the bus */
- iotg->hsm.a_bus_req = 1;
- iotg->hsm.a_bus_drop = 0;
- /* defautly don't request bus as B device */
- iotg->hsm.b_bus_req = 0;
- /* no system error */
- iotg->hsm.a_clr_err = 0;
-
- langwell_otg_phy_low_power_wait(1);
-}
-
-static void update_hsm(void)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 val32;
-
- /* read OTGSC */
- val32 = readl(lnw->iotg.base + CI_OTGSC);
- dev_dbg(lnw->dev, "%s: OTGSC value = 0x%x\n", __func__, val32);
-
- iotg->hsm.id = !!(val32 & OTGSC_ID);
- iotg->hsm.b_sess_end = !!(val32 & OTGSC_BSE);
- iotg->hsm.b_sess_vld = !!(val32 & OTGSC_BSV);
- iotg->hsm.a_sess_vld = !!(val32 & OTGSC_ASV);
- iotg->hsm.a_vbus_vld = !!(val32 & OTGSC_AVV);
-}
-
-static irqreturn_t otg_dummy_irq(int irq, void *_dev)
-{
- struct langwell_otg *lnw = the_transceiver;
- void __iomem *reg_base = _dev;
- u32 val;
- u32 int_mask = 0;
-
- val = readl(reg_base + CI_USBMODE);
- if ((val & USBMODE_CM) != USBMODE_DEVICE)
- return IRQ_NONE;
-
- val = readl(reg_base + CI_USBSTS);
- int_mask = val & INTR_DUMMY_MASK;
-
- if (int_mask == 0)
- return IRQ_NONE;
-
- /* clear hsm.b_conn here since host driver can't detect it
- * otg_dummy_irq called means B-disconnect happened.
- */
- if (lnw->iotg.hsm.b_conn) {
- lnw->iotg.hsm.b_conn = 0;
- if (spin_trylock(&lnw->wq_lock)) {
- langwell_update_transceiver();
- spin_unlock(&lnw->wq_lock);
- }
- }
-
- /* Clear interrupts */
- writel(int_mask, reg_base + CI_USBSTS);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t otg_irq(int irq, void *_dev)
-{
- struct langwell_otg *lnw = _dev;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- u32 int_sts, int_en;
- u32 int_mask = 0;
- int flag = 0;
-
- int_sts = readl(lnw->iotg.base + CI_OTGSC);
- int_en = (int_sts & OTGSC_INTEN_MASK) >> 8;
- int_mask = int_sts & int_en;
- if (int_mask == 0)
- return IRQ_NONE;
-
- if (int_mask & OTGSC_IDIS) {
- dev_dbg(lnw->dev, "%s: id change int\n", __func__);
- iotg->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0;
- dev_dbg(lnw->dev, "id = %d\n", iotg->hsm.id);
- flag = 1;
- }
- if (int_mask & OTGSC_DPIS) {
- dev_dbg(lnw->dev, "%s: data pulse int\n", __func__);
- iotg->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0;
- dev_dbg(lnw->dev, "data pulse = %d\n", iotg->hsm.a_srp_det);
- flag = 1;
- }
- if (int_mask & OTGSC_BSEIS) {
- dev_dbg(lnw->dev, "%s: b session end int\n", __func__);
- iotg->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0;
- dev_dbg(lnw->dev, "b_sess_end = %d\n", iotg->hsm.b_sess_end);
- flag = 1;
- }
- if (int_mask & OTGSC_BSVIS) {
- dev_dbg(lnw->dev, "%s: b session valid int\n", __func__);
- iotg->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0;
- dev_dbg(lnw->dev, "b_sess_vld = %d\n", iotg->hsm.b_sess_end);
- flag = 1;
- }
- if (int_mask & OTGSC_ASVIS) {
- dev_dbg(lnw->dev, "%s: a session valid int\n", __func__);
- iotg->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0;
- dev_dbg(lnw->dev, "a_sess_vld = %d\n", iotg->hsm.a_sess_vld);
- flag = 1;
- }
- if (int_mask & OTGSC_AVVIS) {
- dev_dbg(lnw->dev, "%s: a vbus valid int\n", __func__);
- iotg->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0;
- dev_dbg(lnw->dev, "a_vbus_vld = %d\n", iotg->hsm.a_vbus_vld);
- flag = 1;
- }
-
- if (int_mask & OTGSC_1MSS) {
- /* need to schedule otg_work if any timer is expired */
- if (langwell_otg_tick_timer(&int_sts))
- flag = 1;
- }
-
- writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask,
- lnw->iotg.base + CI_OTGSC);
- if (flag)
- langwell_update_transceiver();
-
- return IRQ_HANDLED;
-}
-
-static int langwell_otg_iotg_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = data;
- int flag = 0;
-
- if (iotg == NULL)
- return NOTIFY_BAD;
-
- if (lnw == NULL)
- return NOTIFY_BAD;
-
- switch (action) {
- case MID_OTG_NOTIFY_CONNECT:
- dev_dbg(lnw->dev, "Lnw OTG Notify Connect Event\n");
- if (iotg->otg.default_a == 1)
- iotg->hsm.b_conn = 1;
- else
- iotg->hsm.a_conn = 1;
- flag = 1;
- break;
- case MID_OTG_NOTIFY_DISCONN:
- dev_dbg(lnw->dev, "Lnw OTG Notify Disconnect Event\n");
- if (iotg->otg.default_a == 1)
- iotg->hsm.b_conn = 0;
- else
- iotg->hsm.a_conn = 0;
- flag = 1;
- break;
- case MID_OTG_NOTIFY_HSUSPEND:
- dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus suspend Event\n");
- if (iotg->otg.default_a == 1)
- iotg->hsm.a_suspend_req = 1;
- else
- iotg->hsm.b_bus_req = 0;
- flag = 1;
- break;
- case MID_OTG_NOTIFY_HRESUME:
- dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus resume Event\n");
- if (iotg->otg.default_a == 1)
- iotg->hsm.b_bus_resume = 1;
- flag = 1;
- break;
- case MID_OTG_NOTIFY_CSUSPEND:
- dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus suspend Event\n");
- if (iotg->otg.default_a == 1) {
- if (iotg->hsm.b_bus_suspend_vld == 2) {
- iotg->hsm.b_bus_suspend = 1;
- iotg->hsm.b_bus_suspend_vld = 0;
- flag = 1;
- } else {
- iotg->hsm.b_bus_suspend_vld++;
- flag = 0;
- }
- } else {
- if (iotg->hsm.a_bus_suspend == 0) {
- iotg->hsm.a_bus_suspend = 1;
- flag = 1;
- }
- }
- break;
- case MID_OTG_NOTIFY_CRESUME:
- dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus resume Event\n");
- if (iotg->otg.default_a == 0)
- iotg->hsm.a_bus_suspend = 0;
- flag = 0;
- break;
- case MID_OTG_NOTIFY_HOSTADD:
- dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver Add\n");
- flag = 1;
- break;
- case MID_OTG_NOTIFY_HOSTREMOVE:
- dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver remove\n");
- flag = 1;
- break;
- case MID_OTG_NOTIFY_CLIENTADD:
- dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver Add\n");
- flag = 1;
- break;
- case MID_OTG_NOTIFY_CLIENTREMOVE:
- dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver remove\n");
- flag = 1;
- break;
- default:
- dev_dbg(lnw->dev, "Lnw OTG Nofity unknown notify message\n");
- return NOTIFY_DONE;
- }
-
- if (flag)
- langwell_update_transceiver();
-
- return NOTIFY_OK;
-}
-
-static void langwell_otg_work(struct work_struct *work)
-{
- struct langwell_otg *lnw;
- struct intel_mid_otg_xceiv *iotg;
- int retval;
- struct pci_dev *pdev;
-
- lnw = container_of(work, struct langwell_otg, work);
- iotg = &lnw->iotg;
- pdev = to_pci_dev(lnw->dev);
-
- dev_dbg(lnw->dev, "%s: old state = %s\n", __func__,
- otg_state_string(iotg->otg.state));
-
- switch (iotg->otg.state) {
- case OTG_STATE_UNDEFINED:
- case OTG_STATE_B_IDLE:
- if (!iotg->hsm.id) {
- langwell_otg_del_timer(b_srp_init_tmr);
- del_timer_sync(&lnw->hsm_timer);
-
- iotg->otg.default_a = 1;
- iotg->hsm.a_srp_det = 0;
-
- langwell_otg_chrg_vbus(0);
- set_host_mode();
- langwell_otg_phy_low_power(1);
-
- iotg->otg.state = OTG_STATE_A_IDLE;
- langwell_update_transceiver();
- } else if (iotg->hsm.b_sess_vld) {
- langwell_otg_del_timer(b_srp_init_tmr);
- del_timer_sync(&lnw->hsm_timer);
- iotg->hsm.b_sess_end = 0;
- iotg->hsm.a_bus_suspend = 0;
- langwell_otg_chrg_vbus(0);
-
- if (lnw->iotg.start_peripheral) {
- lnw->iotg.start_peripheral(&lnw->iotg);
- iotg->otg.state = OTG_STATE_B_PERIPHERAL;
- } else
- dev_dbg(lnw->dev, "client driver not loaded\n");
-
- } else if (iotg->hsm.b_srp_init_tmout) {
- iotg->hsm.b_srp_init_tmout = 0;
- dev_warn(lnw->dev, "SRP init timeout\n");
- } else if (iotg->hsm.b_srp_fail_tmout) {
- iotg->hsm.b_srp_fail_tmout = 0;
- iotg->hsm.b_bus_req = 0;
-
- /* No silence failure */
- langwell_otg_nsf_msg(6);
- } else if (iotg->hsm.b_bus_req && iotg->hsm.b_sess_end) {
- del_timer_sync(&lnw->hsm_timer);
- /* workaround for b_se0_srp detection */
- retval = langwell_otg_check_se0_srp(0);
- if (retval) {
- iotg->hsm.b_bus_req = 0;
- dev_dbg(lnw->dev, "LS isn't SE0, try later\n");
- } else {
- /* clear the PHCD before start srp */
- langwell_otg_phy_low_power(0);
-
- /* Start SRP */
- langwell_otg_add_timer(b_srp_init_tmr);
- iotg->otg.start_srp(&iotg->otg);
- langwell_otg_del_timer(b_srp_init_tmr);
- langwell_otg_add_ktimer(TB_SRP_FAIL_TMR);
-
- /* reset PHY low power mode here */
- langwell_otg_phy_low_power_wait(1);
- }
- }
- break;
- case OTG_STATE_B_SRP_INIT:
- if (!iotg->hsm.id) {
- iotg->otg.default_a = 1;
- iotg->hsm.a_srp_det = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- langwell_otg_chrg_vbus(0);
- set_host_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_A_IDLE;
- langwell_update_transceiver();
- } else if (iotg->hsm.b_sess_vld) {
- langwell_otg_chrg_vbus(0);
- if (lnw->iotg.start_peripheral) {
- lnw->iotg.start_peripheral(&lnw->iotg);
- iotg->otg.state = OTG_STATE_B_PERIPHERAL;
- } else
- dev_dbg(lnw->dev, "client driver not loaded\n");
- }
- break;
- case OTG_STATE_B_PERIPHERAL:
- if (!iotg->hsm.id) {
- iotg->otg.default_a = 1;
- iotg->hsm.a_srp_det = 0;
-
- langwell_otg_chrg_vbus(0);
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- set_host_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_A_IDLE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.b_sess_vld) {
- iotg->hsm.b_hnp_enable = 0;
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- iotg->otg.state = OTG_STATE_B_IDLE;
- } else if (iotg->hsm.b_bus_req && iotg->otg.gadget &&
- iotg->otg.gadget->b_hnp_enable &&
- iotg->hsm.a_bus_suspend) {
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- langwell_otg_HAAR(1);
- iotg->hsm.a_conn = 0;
-
- if (lnw->iotg.start_host) {
- lnw->iotg.start_host(&lnw->iotg);
- iotg->otg.state = OTG_STATE_B_WAIT_ACON;
- } else
- dev_dbg(lnw->dev,
- "host driver not loaded.\n");
-
- iotg->hsm.a_bus_resume = 0;
- langwell_otg_add_ktimer(TB_ASE0_BRST_TMR);
- }
- break;
-
- case OTG_STATE_B_WAIT_ACON:
- if (!iotg->hsm.id) {
- /* delete hsm timer for b_ase0_brst_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- iotg->otg.default_a = 1;
- iotg->hsm.a_srp_det = 0;
-
- langwell_otg_chrg_vbus(0);
-
- langwell_otg_HAAR(0);
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- set_host_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_A_IDLE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.b_sess_vld) {
- /* delete hsm timer for b_ase0_brst_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- iotg->hsm.b_hnp_enable = 0;
- iotg->hsm.b_bus_req = 0;
-
- langwell_otg_chrg_vbus(0);
- langwell_otg_HAAR(0);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- set_client_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- } else if (iotg->hsm.a_conn) {
- /* delete hsm timer for b_ase0_brst_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- langwell_otg_HAAR(0);
- iotg->otg.state = OTG_STATE_B_HOST;
- langwell_update_transceiver();
- } else if (iotg->hsm.a_bus_resume ||
- iotg->hsm.b_ase0_brst_tmout) {
- /* delete hsm timer for b_ase0_brst_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- langwell_otg_HAAR(0);
- langwell_otg_nsf_msg(7);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- iotg->hsm.a_bus_suspend = 0;
- iotg->hsm.b_bus_req = 0;
-
- if (lnw->iotg.start_peripheral)
- lnw->iotg.start_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver not loaded.\n");
-
- iotg->otg.state = OTG_STATE_B_PERIPHERAL;
- }
- break;
-
- case OTG_STATE_B_HOST:
- if (!iotg->hsm.id) {
- iotg->otg.default_a = 1;
- iotg->hsm.a_srp_det = 0;
-
- langwell_otg_chrg_vbus(0);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- set_host_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_A_IDLE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.b_sess_vld) {
- iotg->hsm.b_hnp_enable = 0;
- iotg->hsm.b_bus_req = 0;
-
- langwell_otg_chrg_vbus(0);
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- set_client_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- } else if ((!iotg->hsm.b_bus_req) ||
- (!iotg->hsm.a_conn)) {
- iotg->hsm.b_bus_req = 0;
- langwell_otg_loc_sof(0);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- iotg->hsm.a_bus_suspend = 0;
-
- if (lnw->iotg.start_peripheral)
- lnw->iotg.start_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver not loaded.\n");
-
- iotg->otg.state = OTG_STATE_B_PERIPHERAL;
- }
- break;
-
- case OTG_STATE_A_IDLE:
- iotg->otg.default_a = 1;
- if (iotg->hsm.id) {
- iotg->otg.default_a = 0;
- iotg->hsm.b_bus_req = 0;
- iotg->hsm.vbus_srp_up = 0;
-
- langwell_otg_chrg_vbus(0);
- set_client_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.a_bus_drop &&
- (iotg->hsm.a_srp_det || iotg->hsm.a_bus_req)) {
- langwell_otg_phy_low_power(0);
-
- /* Turn on VBus */
- iotg->otg.set_vbus(&iotg->otg, true);
-
- iotg->hsm.vbus_srp_up = 0;
- iotg->hsm.a_wait_vrise_tmout = 0;
- langwell_otg_add_timer(a_wait_vrise_tmr);
- iotg->otg.state = OTG_STATE_A_WAIT_VRISE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.a_bus_drop && iotg->hsm.a_sess_vld) {
- iotg->hsm.vbus_srp_up = 1;
- } else if (!iotg->hsm.a_sess_vld && iotg->hsm.vbus_srp_up) {
- msleep(10);
- langwell_otg_phy_low_power(0);
-
- /* Turn on VBus */
- iotg->otg.set_vbus(&iotg->otg, true);
- iotg->hsm.a_srp_det = 1;
- iotg->hsm.vbus_srp_up = 0;
- iotg->hsm.a_wait_vrise_tmout = 0;
- langwell_otg_add_timer(a_wait_vrise_tmr);
- iotg->otg.state = OTG_STATE_A_WAIT_VRISE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.a_sess_vld &&
- !iotg->hsm.vbus_srp_up) {
- langwell_otg_phy_low_power(1);
- }
- break;
- case OTG_STATE_A_WAIT_VRISE:
- if (iotg->hsm.id) {
- langwell_otg_del_timer(a_wait_vrise_tmr);
- iotg->hsm.b_bus_req = 0;
- iotg->otg.default_a = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- set_client_mode();
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- } else if (iotg->hsm.a_vbus_vld) {
- langwell_otg_del_timer(a_wait_vrise_tmr);
- iotg->hsm.b_conn = 0;
- if (lnw->iotg.start_host)
- lnw->iotg.start_host(&lnw->iotg);
- else {
- dev_dbg(lnw->dev, "host driver not loaded.\n");
- break;
- }
-
- langwell_otg_add_ktimer(TA_WAIT_BCON_TMR);
- iotg->otg.state = OTG_STATE_A_WAIT_BCON;
- } else if (iotg->hsm.a_wait_vrise_tmout) {
- iotg->hsm.b_conn = 0;
- if (iotg->hsm.a_vbus_vld) {
- if (lnw->iotg.start_host)
- lnw->iotg.start_host(&lnw->iotg);
- else {
- dev_dbg(lnw->dev,
- "host driver not loaded.\n");
- break;
- }
- langwell_otg_add_ktimer(TA_WAIT_BCON_TMR);
- iotg->otg.state = OTG_STATE_A_WAIT_BCON;
- } else {
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_A_VBUS_ERR;
- }
- }
- break;
- case OTG_STATE_A_WAIT_BCON:
- if (iotg->hsm.id) {
- /* delete hsm timer for a_wait_bcon_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- iotg->otg.default_a = 0;
- iotg->hsm.b_bus_req = 0;
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- set_client_mode();
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.a_vbus_vld) {
- /* delete hsm timer for a_wait_bcon_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_A_VBUS_ERR;
- } else if (iotg->hsm.a_bus_drop ||
- (iotg->hsm.a_wait_bcon_tmout &&
- !iotg->hsm.a_bus_req)) {
- /* delete hsm timer for a_wait_bcon_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_WAIT_VFALL;
- } else if (iotg->hsm.b_conn) {
- /* delete hsm timer for a_wait_bcon_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- iotg->hsm.a_suspend_req = 0;
- iotg->otg.state = OTG_STATE_A_HOST;
- if (iotg->hsm.a_srp_det && iotg->otg.host &&
- !iotg->otg.host->b_hnp_enable) {
- /* SRP capable peripheral-only device */
- iotg->hsm.a_bus_req = 1;
- iotg->hsm.a_srp_det = 0;
- } else if (!iotg->hsm.a_bus_req && iotg->otg.host &&
- iotg->otg.host->b_hnp_enable) {
- /* It is not safe enough to do a fast
- * transition from A_WAIT_BCON to
- * A_SUSPEND */
- msleep(10000);
- if (iotg->hsm.a_bus_req)
- break;
-
- if (request_irq(pdev->irq,
- otg_dummy_irq, IRQF_SHARED,
- driver_name, iotg->base) != 0) {
- dev_dbg(lnw->dev,
- "request interrupt %d fail\n",
- pdev->irq);
- }
-
- langwell_otg_HABA(1);
- iotg->hsm.b_bus_resume = 0;
- iotg->hsm.a_aidl_bdis_tmout = 0;
-
- langwell_otg_loc_sof(0);
- /* clear PHCD to enable HW timer */
- langwell_otg_phy_low_power(0);
- langwell_otg_add_timer(a_aidl_bdis_tmr);
- iotg->otg.state = OTG_STATE_A_SUSPEND;
- } else if (!iotg->hsm.a_bus_req && iotg->otg.host &&
- !iotg->otg.host->b_hnp_enable) {
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_WAIT_VFALL;
- }
- }
- break;
- case OTG_STATE_A_HOST:
- if (iotg->hsm.id) {
- iotg->otg.default_a = 0;
- iotg->hsm.b_bus_req = 0;
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- set_client_mode();
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (iotg->hsm.a_bus_drop ||
- (iotg->otg.host &&
- !iotg->otg.host->b_hnp_enable &&
- !iotg->hsm.a_bus_req)) {
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_WAIT_VFALL;
- } else if (!iotg->hsm.a_vbus_vld) {
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_A_VBUS_ERR;
- } else if (iotg->otg.host &&
- iotg->otg.host->b_hnp_enable &&
- !iotg->hsm.a_bus_req) {
- /* Set HABA to enable hardware assistance to signal
- * A-connect after receiver B-disconnect. Hardware
- * will then set client mode and enable URE, SLE and
- * PCE after the assistance. otg_dummy_irq is used to
- * clean these ints when client driver is not resumed.
- */
- if (request_irq(pdev->irq, otg_dummy_irq, IRQF_SHARED,
- driver_name, iotg->base) != 0) {
- dev_dbg(lnw->dev,
- "request interrupt %d failed\n",
- pdev->irq);
- }
-
- /* set HABA */
- langwell_otg_HABA(1);
- iotg->hsm.b_bus_resume = 0;
- iotg->hsm.a_aidl_bdis_tmout = 0;
- langwell_otg_loc_sof(0);
- /* clear PHCD to enable HW timer */
- langwell_otg_phy_low_power(0);
- langwell_otg_add_timer(a_aidl_bdis_tmr);
- iotg->otg.state = OTG_STATE_A_SUSPEND;
- } else if (!iotg->hsm.b_conn || !iotg->hsm.a_bus_req) {
- langwell_otg_add_ktimer(TA_WAIT_BCON_TMR);
- iotg->otg.state = OTG_STATE_A_WAIT_BCON;
- }
- break;
- case OTG_STATE_A_SUSPEND:
- if (iotg->hsm.id) {
- langwell_otg_del_timer(a_aidl_bdis_tmr);
- langwell_otg_HABA(0);
- free_irq(pdev->irq, iotg->base);
- iotg->otg.default_a = 0;
- iotg->hsm.b_bus_req = 0;
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- set_client_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (iotg->hsm.a_bus_req ||
- iotg->hsm.b_bus_resume) {
- langwell_otg_del_timer(a_aidl_bdis_tmr);
- langwell_otg_HABA(0);
- free_irq(pdev->irq, iotg->base);
- iotg->hsm.a_suspend_req = 0;
- langwell_otg_loc_sof(1);
- iotg->otg.state = OTG_STATE_A_HOST;
- } else if (iotg->hsm.a_aidl_bdis_tmout ||
- iotg->hsm.a_bus_drop) {
- langwell_otg_del_timer(a_aidl_bdis_tmr);
- langwell_otg_HABA(0);
- free_irq(pdev->irq, iotg->base);
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_WAIT_VFALL;
- } else if (!iotg->hsm.b_conn && iotg->otg.host &&
- iotg->otg.host->b_hnp_enable) {
- langwell_otg_del_timer(a_aidl_bdis_tmr);
- langwell_otg_HABA(0);
- free_irq(pdev->irq, iotg->base);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- iotg->hsm.b_bus_suspend = 0;
- iotg->hsm.b_bus_suspend_vld = 0;
-
- /* msleep(200); */
- if (lnw->iotg.start_peripheral)
- lnw->iotg.start_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver not loaded.\n");
-
- langwell_otg_add_ktimer(TB_BUS_SUSPEND_TMR);
- iotg->otg.state = OTG_STATE_A_PERIPHERAL;
- break;
- } else if (!iotg->hsm.a_vbus_vld) {
- langwell_otg_del_timer(a_aidl_bdis_tmr);
- langwell_otg_HABA(0);
- free_irq(pdev->irq, iotg->base);
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_A_VBUS_ERR;
- }
- break;
- case OTG_STATE_A_PERIPHERAL:
- if (iotg->hsm.id) {
- /* delete hsm timer for b_bus_suspend_tmr */
- del_timer_sync(&lnw->hsm_timer);
- iotg->otg.default_a = 0;
- iotg->hsm.b_bus_req = 0;
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- set_client_mode();
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (!iotg->hsm.a_vbus_vld) {
- /* delete hsm timer for b_bus_suspend_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- langwell_otg_phy_low_power_wait(1);
- iotg->otg.state = OTG_STATE_A_VBUS_ERR;
- } else if (iotg->hsm.a_bus_drop) {
- /* delete hsm timer for b_bus_suspend_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_WAIT_VFALL;
- } else if (iotg->hsm.b_bus_suspend) {
- /* delete hsm timer for b_bus_suspend_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- if (lnw->iotg.start_host)
- lnw->iotg.start_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver not loaded.\n");
- langwell_otg_add_ktimer(TA_WAIT_BCON_TMR);
- iotg->otg.state = OTG_STATE_A_WAIT_BCON;
- } else if (iotg->hsm.b_bus_suspend_tmout) {
- u32 val;
- val = readl(lnw->iotg.base + CI_PORTSC1);
- if (!(val & PORTSC_SUSP))
- break;
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "client driver has been removed.\n");
-
- if (lnw->iotg.start_host)
- lnw->iotg.start_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev,
- "host driver not loaded.\n");
- langwell_otg_add_ktimer(TA_WAIT_BCON_TMR);
- iotg->otg.state = OTG_STATE_A_WAIT_BCON;
- }
- break;
- case OTG_STATE_A_VBUS_ERR:
- if (iotg->hsm.id) {
- iotg->otg.default_a = 0;
- iotg->hsm.a_clr_err = 0;
- iotg->hsm.a_srp_det = 0;
- set_client_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (iotg->hsm.a_clr_err) {
- iotg->hsm.a_clr_err = 0;
- iotg->hsm.a_srp_det = 0;
- reset_otg();
- init_hsm();
- if (iotg->otg.state == OTG_STATE_A_IDLE)
- langwell_update_transceiver();
- } else {
- /* FW will clear PHCD bit when any VBus
- * event detected. Reset PHCD to 1 again */
- langwell_otg_phy_low_power(1);
- }
- break;
- case OTG_STATE_A_WAIT_VFALL:
- if (iotg->hsm.id) {
- iotg->otg.default_a = 0;
- set_client_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_B_IDLE;
- langwell_update_transceiver();
- } else if (iotg->hsm.a_bus_req) {
-
- /* Turn on VBus */
- iotg->otg.set_vbus(&iotg->otg, true);
- iotg->hsm.a_wait_vrise_tmout = 0;
- langwell_otg_add_timer(a_wait_vrise_tmr);
- iotg->otg.state = OTG_STATE_A_WAIT_VRISE;
- } else if (!iotg->hsm.a_sess_vld) {
- iotg->hsm.a_srp_det = 0;
- set_host_mode();
- langwell_otg_phy_low_power(1);
- iotg->otg.state = OTG_STATE_A_IDLE;
- }
- break;
- default:
- ;
- }
-
- dev_dbg(lnw->dev, "%s: new state = %s\n", __func__,
- otg_state_string(iotg->otg.state));
-}
-
-static ssize_t
-show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
-{
- struct langwell_otg *lnw = the_transceiver;
- char *next;
- unsigned size, t;
-
- next = buf;
- size = PAGE_SIZE;
-
- t = scnprintf(next, size,
- "\n"
- "USBCMD = 0x%08x\n"
- "USBSTS = 0x%08x\n"
- "USBINTR = 0x%08x\n"
- "ASYNCLISTADDR = 0x%08x\n"
- "PORTSC1 = 0x%08x\n"
- "HOSTPC1 = 0x%08x\n"
- "OTGSC = 0x%08x\n"
- "USBMODE = 0x%08x\n",
- readl(lnw->iotg.base + 0x30),
- readl(lnw->iotg.base + 0x34),
- readl(lnw->iotg.base + 0x38),
- readl(lnw->iotg.base + 0x48),
- readl(lnw->iotg.base + 0x74),
- readl(lnw->iotg.base + 0xb4),
- readl(lnw->iotg.base + 0xf4),
- readl(lnw->iotg.base + 0xf8)
- );
- size -= t;
- next += t;
-
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
-
-static ssize_t
-show_hsm(struct device *_dev, struct device_attribute *attr, char *buf)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- char *next;
- unsigned size, t;
-
- next = buf;
- size = PAGE_SIZE;
-
- if (iotg->otg.host)
- iotg->hsm.a_set_b_hnp_en = iotg->otg.host->b_hnp_enable;
-
- if (iotg->otg.gadget)
- iotg->hsm.b_hnp_enable = iotg->otg.gadget->b_hnp_enable;
-
- t = scnprintf(next, size,
- "\n"
- "current state = %s\n"
- "a_bus_resume = \t%d\n"
- "a_bus_suspend = \t%d\n"
- "a_conn = \t%d\n"
- "a_sess_vld = \t%d\n"
- "a_srp_det = \t%d\n"
- "a_vbus_vld = \t%d\n"
- "b_bus_resume = \t%d\n"
- "b_bus_suspend = \t%d\n"
- "b_conn = \t%d\n"
- "b_se0_srp = \t%d\n"
- "b_sess_end = \t%d\n"
- "b_sess_vld = \t%d\n"
- "id = \t%d\n"
- "a_set_b_hnp_en = \t%d\n"
- "b_srp_done = \t%d\n"
- "b_hnp_enable = \t%d\n"
- "a_wait_vrise_tmout = \t%d\n"
- "a_wait_bcon_tmout = \t%d\n"
- "a_aidl_bdis_tmout = \t%d\n"
- "b_ase0_brst_tmout = \t%d\n"
- "a_bus_drop = \t%d\n"
- "a_bus_req = \t%d\n"
- "a_clr_err = \t%d\n"
- "a_suspend_req = \t%d\n"
- "b_bus_req = \t%d\n"
- "b_bus_suspend_tmout = \t%d\n"
- "b_bus_suspend_vld = \t%d\n",
- otg_state_string(iotg->otg.state),
- iotg->hsm.a_bus_resume,
- iotg->hsm.a_bus_suspend,
- iotg->hsm.a_conn,
- iotg->hsm.a_sess_vld,
- iotg->hsm.a_srp_det,
- iotg->hsm.a_vbus_vld,
- iotg->hsm.b_bus_resume,
- iotg->hsm.b_bus_suspend,
- iotg->hsm.b_conn,
- iotg->hsm.b_se0_srp,
- iotg->hsm.b_sess_end,
- iotg->hsm.b_sess_vld,
- iotg->hsm.id,
- iotg->hsm.a_set_b_hnp_en,
- iotg->hsm.b_srp_done,
- iotg->hsm.b_hnp_enable,
- iotg->hsm.a_wait_vrise_tmout,
- iotg->hsm.a_wait_bcon_tmout,
- iotg->hsm.a_aidl_bdis_tmout,
- iotg->hsm.b_ase0_brst_tmout,
- iotg->hsm.a_bus_drop,
- iotg->hsm.a_bus_req,
- iotg->hsm.a_clr_err,
- iotg->hsm.a_suspend_req,
- iotg->hsm.b_bus_req,
- iotg->hsm.b_bus_suspend_tmout,
- iotg->hsm.b_bus_suspend_vld
- );
- size -= t;
- next += t;
-
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL);
-
-static ssize_t
-get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct langwell_otg *lnw = the_transceiver;
- char *next;
- unsigned size, t;
-
- next = buf;
- size = PAGE_SIZE;
-
- t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_req);
- size -= t;
- next += t;
-
- return PAGE_SIZE - size;
-}
-
-static ssize_t
-set_a_bus_req(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
-
- if (!iotg->otg.default_a)
- return -1;
- if (count > 2)
- return -1;
-
- if (buf[0] == '0') {
- iotg->hsm.a_bus_req = 0;
- dev_dbg(lnw->dev, "User request: a_bus_req = 0\n");
- } else if (buf[0] == '1') {
- /* If a_bus_drop is TRUE, a_bus_req can't be set */
- if (iotg->hsm.a_bus_drop)
- return -1;
- iotg->hsm.a_bus_req = 1;
- dev_dbg(lnw->dev, "User request: a_bus_req = 1\n");
- }
- if (spin_trylock(&lnw->wq_lock)) {
- langwell_update_transceiver();
- spin_unlock(&lnw->wq_lock);
- }
- return count;
-}
-static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req);
-
-static ssize_t
-get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct langwell_otg *lnw = the_transceiver;
- char *next;
- unsigned size, t;
-
- next = buf;
- size = PAGE_SIZE;
-
- t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_drop);
- size -= t;
- next += t;
-
- return PAGE_SIZE - size;
-}
-
-static ssize_t
-set_a_bus_drop(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
-
- if (!iotg->otg.default_a)
- return -1;
- if (count > 2)
- return -1;
-
- if (buf[0] == '0') {
- iotg->hsm.a_bus_drop = 0;
- dev_dbg(lnw->dev, "User request: a_bus_drop = 0\n");
- } else if (buf[0] == '1') {
- iotg->hsm.a_bus_drop = 1;
- iotg->hsm.a_bus_req = 0;
- dev_dbg(lnw->dev, "User request: a_bus_drop = 1\n");
- dev_dbg(lnw->dev, "User request: and a_bus_req = 0\n");
- }
- if (spin_trylock(&lnw->wq_lock)) {
- langwell_update_transceiver();
- spin_unlock(&lnw->wq_lock);
- }
- return count;
-}
-static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, set_a_bus_drop);
-
-static ssize_t
-get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct langwell_otg *lnw = the_transceiver;
- char *next;
- unsigned size, t;
-
- next = buf;
- size = PAGE_SIZE;
-
- t = scnprintf(next, size, "%d", lnw->iotg.hsm.b_bus_req);
- size -= t;
- next += t;
-
- return PAGE_SIZE - size;
-}
-
-static ssize_t
-set_b_bus_req(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
-
- if (iotg->otg.default_a)
- return -1;
-
- if (count > 2)
- return -1;
-
- if (buf[0] == '0') {
- iotg->hsm.b_bus_req = 0;
- dev_dbg(lnw->dev, "User request: b_bus_req = 0\n");
- } else if (buf[0] == '1') {
- iotg->hsm.b_bus_req = 1;
- dev_dbg(lnw->dev, "User request: b_bus_req = 1\n");
- }
- if (spin_trylock(&lnw->wq_lock)) {
- langwell_update_transceiver();
- spin_unlock(&lnw->wq_lock);
- }
- return count;
-}
-static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req);
-
-static ssize_t
-set_a_clr_err(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
-
- if (!iotg->otg.default_a)
- return -1;
- if (count > 2)
- return -1;
-
- if (buf[0] == '1') {
- iotg->hsm.a_clr_err = 1;
- dev_dbg(lnw->dev, "User request: a_clr_err = 1\n");
- }
- if (spin_trylock(&lnw->wq_lock)) {
- langwell_update_transceiver();
- spin_unlock(&lnw->wq_lock);
- }
- return count;
-}
-static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
-
-static struct attribute *inputs_attrs[] = {
- &dev_attr_a_bus_req.attr,
- &dev_attr_a_bus_drop.attr,
- &dev_attr_b_bus_req.attr,
- &dev_attr_a_clr_err.attr,
- NULL,
-};
-
-static struct attribute_group debug_dev_attr_group = {
- .name = "inputs",
- .attrs = inputs_attrs,
-};
-
-static int langwell_otg_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- unsigned long resource, len;
- void __iomem *base = NULL;
- int retval;
- u32 val32;
- struct langwell_otg *lnw;
- char qname[] = "langwell_otg_queue";
-
- retval = 0;
- dev_dbg(&pdev->dev, "\notg controller is detected.\n");
- if (pci_enable_device(pdev) < 0) {
- retval = -ENODEV;
- goto done;
- }
-
- lnw = kzalloc(sizeof *lnw, GFP_KERNEL);
- if (lnw == NULL) {
- retval = -ENOMEM;
- goto done;
- }
- the_transceiver = lnw;
-
- /* control register: BAR 0 */
- resource = pci_resource_start(pdev, 0);
- len = pci_resource_len(pdev, 0);
- if (!request_mem_region(resource, len, driver_name)) {
- retval = -EBUSY;
- goto err;
- }
- lnw->region = 1;
-
- base = ioremap_nocache(resource, len);
- if (base == NULL) {
- retval = -EFAULT;
- goto err;
- }
- lnw->iotg.base = base;
-
- if (!request_mem_region(USBCFG_ADDR, USBCFG_LEN, driver_name)) {
- retval = -EBUSY;
- goto err;
- }
- lnw->cfg_region = 1;
-
- /* For the SCCB.USBCFG register */
- base = ioremap_nocache(USBCFG_ADDR, USBCFG_LEN);
- if (base == NULL) {
- retval = -EFAULT;
- goto err;
- }
- lnw->usbcfg = base;
-
- if (!pdev->irq) {
- dev_dbg(&pdev->dev, "No IRQ.\n");
- retval = -ENODEV;
- goto err;
- }
-
- lnw->qwork = create_singlethread_workqueue(qname);
- if (!lnw->qwork) {
- dev_dbg(&pdev->dev, "cannot create workqueue %s\n", qname);
- retval = -ENOMEM;
- goto err;
- }
- INIT_WORK(&lnw->work, langwell_otg_work);
-
- /* OTG common part */
- lnw->dev = &pdev->dev;
- lnw->iotg.otg.dev = lnw->dev;
- lnw->iotg.otg.label = driver_name;
- lnw->iotg.otg.set_host = langwell_otg_set_host;
- lnw->iotg.otg.set_peripheral = langwell_otg_set_peripheral;
- lnw->iotg.otg.set_power = langwell_otg_set_power;
- lnw->iotg.otg.set_vbus = langwell_otg_set_vbus;
- lnw->iotg.otg.start_srp = langwell_otg_start_srp;
- lnw->iotg.otg.state = OTG_STATE_UNDEFINED;
-
- if (otg_set_transceiver(&lnw->iotg.otg)) {
- dev_dbg(lnw->dev, "can't set transceiver\n");
- retval = -EBUSY;
- goto err;
- }
-
- reset_otg();
- init_hsm();
-
- spin_lock_init(&lnw->lock);
- spin_lock_init(&lnw->wq_lock);
- INIT_LIST_HEAD(&active_timers);
- retval = langwell_otg_init_timers(&lnw->iotg.hsm);
- if (retval) {
- dev_dbg(&pdev->dev, "Failed to init timers\n");
- goto err;
- }
-
- init_timer(&lnw->hsm_timer);
- ATOMIC_INIT_NOTIFIER_HEAD(&lnw->iotg.iotg_notifier);
-
- lnw->iotg_notifier.notifier_call = langwell_otg_iotg_notify;
-
- retval = intel_mid_otg_register_notifier(&lnw->iotg,
- &lnw->iotg_notifier);
- if (retval) {
- dev_dbg(lnw->dev, "Failed to register notifier\n");
- goto err;
- }
-
- if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
- driver_name, lnw) != 0) {
- dev_dbg(lnw->dev, "request interrupt %d failed\n", pdev->irq);
- retval = -EBUSY;
- goto err;
- }
-
- /* enable OTGSC int */
- val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE |
- OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU;
- writel(val32, lnw->iotg.base + CI_OTGSC);
-
- retval = device_create_file(&pdev->dev, &dev_attr_registers);
- if (retval < 0) {
- dev_dbg(lnw->dev,
- "Can't register sysfs attribute: %d\n", retval);
- goto err;
- }
-
- retval = device_create_file(&pdev->dev, &dev_attr_hsm);
- if (retval < 0) {
- dev_dbg(lnw->dev, "Can't hsm sysfs attribute: %d\n", retval);
- goto err;
- }
-
- retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group);
- if (retval < 0) {
- dev_dbg(lnw->dev,
- "Can't register sysfs attr group: %d\n", retval);
- goto err;
- }
-
- if (lnw->iotg.otg.state == OTG_STATE_A_IDLE)
- langwell_update_transceiver();
-
- return 0;
-
-err:
- if (the_transceiver)
- langwell_otg_remove(pdev);
-done:
- return retval;
-}
-
-static void langwell_otg_remove(struct pci_dev *pdev)
-{
- struct langwell_otg *lnw = the_transceiver;
-
- if (lnw->qwork) {
- flush_workqueue(lnw->qwork);
- destroy_workqueue(lnw->qwork);
- }
- intel_mid_otg_unregister_notifier(&lnw->iotg, &lnw->iotg_notifier);
- langwell_otg_free_timers();
-
- /* disable OTGSC interrupt as OTGSC doesn't change in reset */
- writel(0, lnw->iotg.base + CI_OTGSC);
-
- if (pdev->irq)
- free_irq(pdev->irq, lnw);
- if (lnw->usbcfg)
- iounmap(lnw->usbcfg);
- if (lnw->cfg_region)
- release_mem_region(USBCFG_ADDR, USBCFG_LEN);
- if (lnw->iotg.base)
- iounmap(lnw->iotg.base);
- if (lnw->region)
- release_mem_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
-
- otg_set_transceiver(NULL);
- pci_disable_device(pdev);
- sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group);
- device_remove_file(&pdev->dev, &dev_attr_hsm);
- device_remove_file(&pdev->dev, &dev_attr_registers);
- kfree(lnw);
- lnw = NULL;
-}
-
-static void transceiver_suspend(struct pci_dev *pdev)
-{
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
- langwell_otg_phy_low_power(1);
-}
-
-static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
-{
- struct langwell_otg *lnw = the_transceiver;
- struct intel_mid_otg_xceiv *iotg = &lnw->iotg;
- int ret = 0;
-
- /* Disbale OTG interrupts */
- langwell_otg_intr(0);
-
- if (pdev->irq)
- free_irq(pdev->irq, lnw);
-
- /* Prevent more otg_work */
- flush_workqueue(lnw->qwork);
- destroy_workqueue(lnw->qwork);
- lnw->qwork = NULL;
-
- /* start actions */
- switch (iotg->otg.state) {
- case OTG_STATE_A_WAIT_VFALL:
- iotg->otg.state = OTG_STATE_A_IDLE;
- case OTG_STATE_A_IDLE:
- case OTG_STATE_B_IDLE:
- case OTG_STATE_A_VBUS_ERR:
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_A_WAIT_VRISE:
- langwell_otg_del_timer(a_wait_vrise_tmr);
- iotg->hsm.a_srp_det = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_A_WAIT_BCON:
- del_timer_sync(&lnw->hsm_timer);
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(&pdev->dev, "host driver has been removed.\n");
-
- iotg->hsm.a_srp_det = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_A_HOST:
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(&pdev->dev, "host driver has been removed.\n");
-
- iotg->hsm.a_srp_det = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
-
- iotg->otg.state = OTG_STATE_A_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_A_SUSPEND:
- langwell_otg_del_timer(a_aidl_bdis_tmr);
- langwell_otg_HABA(0);
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(lnw->dev, "host driver has been removed.\n");
- iotg->hsm.a_srp_det = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_A_PERIPHERAL:
- del_timer_sync(&lnw->hsm_timer);
-
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(&pdev->dev,
- "client driver has been removed.\n");
- iotg->hsm.a_srp_det = 0;
-
- /* Turn off VBus */
- iotg->otg.set_vbus(&iotg->otg, false);
- iotg->otg.state = OTG_STATE_A_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_B_HOST:
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(&pdev->dev, "host driver has been removed.\n");
- iotg->hsm.b_bus_req = 0;
- iotg->otg.state = OTG_STATE_B_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_B_PERIPHERAL:
- if (lnw->iotg.stop_peripheral)
- lnw->iotg.stop_peripheral(&lnw->iotg);
- else
- dev_dbg(&pdev->dev,
- "client driver has been removed.\n");
- iotg->otg.state = OTG_STATE_B_IDLE;
- transceiver_suspend(pdev);
- break;
- case OTG_STATE_B_WAIT_ACON:
- /* delete hsm timer for b_ase0_brst_tmr */
- del_timer_sync(&lnw->hsm_timer);
-
- langwell_otg_HAAR(0);
-
- if (lnw->iotg.stop_host)
- lnw->iotg.stop_host(&lnw->iotg);
- else
- dev_dbg(&pdev->dev, "host driver has been removed.\n");
- iotg->hsm.b_bus_req = 0;
- iotg->otg.state = OTG_STATE_B_IDLE;
- transceiver_suspend(pdev);
- break;
- default:
- dev_dbg(lnw->dev, "error state before suspend\n");
- break;
- }
-
- return ret;
-}
-
-static void transceiver_resume(struct pci_dev *pdev)
-{
- pci_restore_state(pdev);
- pci_set_power_state(pdev, PCI_D0);
-}
-
-static int langwell_otg_resume(struct pci_dev *pdev)
-{
- struct langwell_otg *lnw = the_transceiver;
- int ret = 0;
-
- transceiver_resume(pdev);
-
- lnw->qwork = create_singlethread_workqueue("langwell_otg_queue");
- if (!lnw->qwork) {
- dev_dbg(&pdev->dev, "cannot create langwell otg workqueuen");
- ret = -ENOMEM;
- goto error;
- }
-
- if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
- driver_name, lnw) != 0) {
- dev_dbg(&pdev->dev, "request interrupt %d failed\n", pdev->irq);
- ret = -EBUSY;
- goto error;
- }
-
- /* enable OTG interrupts */
- langwell_otg_intr(1);
-
- update_hsm();
-
- langwell_update_transceiver();
-
- return ret;
-error:
- langwell_otg_intr(0);
- transceiver_suspend(pdev);
- return ret;
-}
-
-static int __init langwell_otg_init(void)
-{
- return pci_register_driver(&otg_pci_driver);
-}
-module_init(langwell_otg_init);
-
-static void __exit langwell_otg_cleanup(void)
-{
- pci_unregister_driver(&otg_pci_driver);
-}
-module_exit(langwell_otg_cleanup);
diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c
new file mode 100644
index 00000000000..b5fbe1452ab
--- /dev/null
+++ b/drivers/usb/otg/mv_otg.c
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ * Author: Chao Xie <chao.xie@marvell.com>
+ * Neil Zhang <zhangwm@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_data/mv_usb.h>
+
+#include "mv_otg.h"
+
+#define DRIVER_DESC "Marvell USB OTG transceiver driver"
+#define DRIVER_VERSION "Jan 20, 2010"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+static const char driver_name[] = "mv-otg";
+
+static char *state_string[] = {
+ "undefined",
+ "b_idle",
+ "b_srp_init",
+ "b_peripheral",
+ "b_wait_acon",
+ "b_host",
+ "a_idle",
+ "a_wait_vrise",
+ "a_wait_bcon",
+ "a_host",
+ "a_suspend",
+ "a_peripheral",
+ "a_wait_vfall",
+ "a_vbus_err"
+};
+
+static int mv_otg_set_vbus(struct otg_transceiver *otg, bool on)
+{
+ struct mv_otg *mvotg = container_of(otg, struct mv_otg, otg);
+ if (mvotg->pdata->set_vbus == NULL)
+ return -ENODEV;
+
+ return mvotg->pdata->set_vbus(on);
+}
+
+static int mv_otg_set_host(struct otg_transceiver *otg,
+ struct usb_bus *host)
+{
+ otg->host = host;
+
+ return 0;
+}
+
+static int mv_otg_set_peripheral(struct otg_transceiver *otg,
+ struct usb_gadget *gadget)
+{
+ otg->gadget = gadget;
+
+ return 0;
+}
+
+static void mv_otg_run_state_machine(struct mv_otg *mvotg,
+ unsigned long delay)
+{
+ dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
+ if (!mvotg->qwork)
+ return;
+
+ queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
+}
+
+static void mv_otg_timer_await_bcon(unsigned long data)
+{
+ struct mv_otg *mvotg = (struct mv_otg *) data;
+
+ mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
+
+ dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
+
+ if (spin_trylock(&mvotg->wq_lock)) {
+ mv_otg_run_state_machine(mvotg, 0);
+ spin_unlock(&mvotg->wq_lock);
+ }
+}
+
+static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
+{
+ struct timer_list *timer;
+
+ if (id >= OTG_TIMER_NUM)
+ return -EINVAL;
+
+ timer = &mvotg->otg_ctrl.timer[id];
+
+ if (timer_pending(timer))
+ del_timer(timer);
+
+ return 0;
+}
+
+static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
+ unsigned long interval,
+ void (*callback) (unsigned long))
+{
+ struct timer_list *timer;
+
+ if (id >= OTG_TIMER_NUM)
+ return -EINVAL;
+
+ timer = &mvotg->otg_ctrl.timer[id];
+ if (timer_pending(timer)) {
+ dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
+ return -EBUSY;
+ }
+
+ init_timer(timer);
+ timer->data = (unsigned long) mvotg;
+ timer->function = callback;
+ timer->expires = jiffies + interval;
+ add_timer(timer);
+
+ return 0;
+}
+
+static int mv_otg_reset(struct mv_otg *mvotg)
+{
+ unsigned int loops;
+ u32 tmp;
+
+ /* Stop the controller */
+ tmp = readl(&mvotg->op_regs->usbcmd);
+ tmp &= ~USBCMD_RUN_STOP;
+ writel(tmp, &mvotg->op_regs->usbcmd);
+
+ /* Reset the controller to get default values */
+ writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
+
+ loops = 500;
+ while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
+ if (loops == 0) {
+ dev_err(&mvotg->pdev->dev,
+ "Wait for RESET completed TIMEOUT\n");
+ return -ETIMEDOUT;
+ }
+ loops--;
+ udelay(20);
+ }
+
+ writel(0x0, &mvotg->op_regs->usbintr);
+ tmp = readl(&mvotg->op_regs->usbsts);
+ writel(tmp, &mvotg->op_regs->usbsts);
+
+ return 0;
+}
+
+static void mv_otg_init_irq(struct mv_otg *mvotg)
+{
+ u32 otgsc;
+
+ mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
+ | OTGSC_INTR_A_VBUS_VALID;
+ mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
+ | OTGSC_INTSTS_A_VBUS_VALID;
+
+ if (mvotg->pdata->vbus == NULL) {
+ mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
+ | OTGSC_INTR_B_SESSION_END;
+ mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
+ | OTGSC_INTSTS_B_SESSION_END;
+ }
+
+ if (mvotg->pdata->id == NULL) {
+ mvotg->irq_en |= OTGSC_INTR_USB_ID;
+ mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
+ }
+
+ otgsc = readl(&mvotg->op_regs->otgsc);
+ otgsc |= mvotg->irq_en;
+ writel(otgsc, &mvotg->op_regs->otgsc);
+}
+
+static void mv_otg_start_host(struct mv_otg *mvotg, int on)
+{
+#ifdef CONFIG_USB
+ struct otg_transceiver *otg = &mvotg->otg;
+ struct usb_hcd *hcd;
+
+ if (!otg->host)
+ return;
+
+ dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
+
+ hcd = bus_to_hcd(otg->host);
+
+ if (on)
+ usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+ else
+ usb_remove_hcd(hcd);
+#endif /* CONFIG_USB */
+}
+
+static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
+{
+ struct otg_transceiver *otg = &mvotg->otg;
+
+ if (!otg->gadget)
+ return;
+
+ dev_info(otg->dev, "gadget %s\n", on ? "on" : "off");
+
+ if (on)
+ usb_gadget_vbus_connect(otg->gadget);
+ else
+ usb_gadget_vbus_disconnect(otg->gadget);
+}
+
+static void otg_clock_enable(struct mv_otg *mvotg)
+{
+ unsigned int i;
+
+ for (i = 0; i < mvotg->clknum; i++)
+ clk_enable(mvotg->clk[i]);
+}
+
+static void otg_clock_disable(struct mv_otg *mvotg)
+{
+ unsigned int i;
+
+ for (i = 0; i < mvotg->clknum; i++)
+ clk_disable(mvotg->clk[i]);
+}
+
+static int mv_otg_enable_internal(struct mv_otg *mvotg)
+{
+ int retval = 0;
+
+ if (mvotg->active)
+ return 0;
+
+ dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
+
+ otg_clock_enable(mvotg);
+ if (mvotg->pdata->phy_init) {
+ retval = mvotg->pdata->phy_init(mvotg->phy_regs);
+ if (retval) {
+ dev_err(&mvotg->pdev->dev,
+ "init phy error %d\n", retval);
+ otg_clock_disable(mvotg);
+ return retval;
+ }
+ }
+ mvotg->active = 1;
+
+ return 0;
+
+}
+
+static int mv_otg_enable(struct mv_otg *mvotg)
+{
+ if (mvotg->clock_gating)
+ return mv_otg_enable_internal(mvotg);
+
+ return 0;
+}
+
+static void mv_otg_disable_internal(struct mv_otg *mvotg)
+{
+ if (mvotg->active) {
+ dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
+ if (mvotg->pdata->phy_deinit)
+ mvotg->pdata->phy_deinit(mvotg->phy_regs);
+ otg_clock_disable(mvotg);
+ mvotg->active = 0;
+ }
+}
+
+static void mv_otg_disable(struct mv_otg *mvotg)
+{
+ if (mvotg->clock_gating)
+ mv_otg_disable_internal(mvotg);
+}
+
+static void mv_otg_update_inputs(struct mv_otg *mvotg)
+{
+ struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+ u32 otgsc;
+
+ otgsc = readl(&mvotg->op_regs->otgsc);
+
+ if (mvotg->pdata->vbus) {
+ if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
+ otg_ctrl->b_sess_vld = 1;
+ otg_ctrl->b_sess_end = 0;
+ } else {
+ otg_ctrl->b_sess_vld = 0;
+ otg_ctrl->b_sess_end = 1;
+ }
+ } else {
+ otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
+ otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
+ }
+
+ if (mvotg->pdata->id)
+ otg_ctrl->id = !!mvotg->pdata->id->poll();
+ else
+ otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
+
+ if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
+ otg_ctrl->a_bus_req = 1;
+
+ otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
+ otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
+
+ dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
+ dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id);
+ dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
+ dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
+ dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
+ dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
+}
+
+static void mv_otg_update_state(struct mv_otg *mvotg)
+{
+ struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+ struct otg_transceiver *otg = &mvotg->otg;
+ int old_state = otg->state;
+
+ switch (old_state) {
+ case OTG_STATE_UNDEFINED:
+ otg->state = OTG_STATE_B_IDLE;
+ /* FALL THROUGH */
+ case OTG_STATE_B_IDLE:
+ if (otg_ctrl->id == 0)
+ otg->state = OTG_STATE_A_IDLE;
+ else if (otg_ctrl->b_sess_vld)
+ otg->state = OTG_STATE_B_PERIPHERAL;
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
+ otg->state = OTG_STATE_B_IDLE;
+ break;
+ case OTG_STATE_A_IDLE:
+ if (otg_ctrl->id)
+ otg->state = OTG_STATE_B_IDLE;
+ else if (!(otg_ctrl->a_bus_drop) &&
+ (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
+ otg->state = OTG_STATE_A_WAIT_VRISE;
+ break;
+ case OTG_STATE_A_WAIT_VRISE:
+ if (otg_ctrl->a_vbus_vld)
+ otg->state = OTG_STATE_A_WAIT_BCON;
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ if (otg_ctrl->id || otg_ctrl->a_bus_drop
+ || otg_ctrl->a_wait_bcon_timeout) {
+ mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+ mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+ otg->state = OTG_STATE_A_WAIT_VFALL;
+ otg_ctrl->a_bus_req = 0;
+ } else if (!otg_ctrl->a_vbus_vld) {
+ mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+ mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+ otg->state = OTG_STATE_A_VBUS_ERR;
+ } else if (otg_ctrl->b_conn) {
+ mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+ mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+ otg->state = OTG_STATE_A_HOST;
+ }
+ break;
+ case OTG_STATE_A_HOST:
+ if (otg_ctrl->id || !otg_ctrl->b_conn
+ || otg_ctrl->a_bus_drop)
+ otg->state = OTG_STATE_A_WAIT_BCON;
+ else if (!otg_ctrl->a_vbus_vld)
+ otg->state = OTG_STATE_A_VBUS_ERR;
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ if (otg_ctrl->id
+ || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
+ || otg_ctrl->a_bus_req)
+ otg->state = OTG_STATE_A_IDLE;
+ break;
+ case OTG_STATE_A_VBUS_ERR:
+ if (otg_ctrl->id || otg_ctrl->a_clr_err
+ || otg_ctrl->a_bus_drop) {
+ otg_ctrl->a_clr_err = 0;
+ otg->state = OTG_STATE_A_WAIT_VFALL;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void mv_otg_work(struct work_struct *work)
+{
+ struct mv_otg *mvotg;
+ struct otg_transceiver *otg;
+ int old_state;
+
+ mvotg = container_of((struct delayed_work *)work, struct mv_otg, work);
+
+run:
+ /* work queue is single thread, or we need spin_lock to protect */
+ otg = &mvotg->otg;
+ old_state = otg->state;
+
+ if (!mvotg->active)
+ return;
+
+ mv_otg_update_inputs(mvotg);
+ mv_otg_update_state(mvotg);
+
+ if (old_state != otg->state) {
+ dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
+ state_string[old_state],
+ state_string[otg->state]);
+
+ switch (otg->state) {
+ case OTG_STATE_B_IDLE:
+ mvotg->otg.default_a = 0;
+ if (old_state == OTG_STATE_B_PERIPHERAL)
+ mv_otg_start_periphrals(mvotg, 0);
+ mv_otg_reset(mvotg);
+ mv_otg_disable(mvotg);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ mv_otg_enable(mvotg);
+ mv_otg_start_periphrals(mvotg, 1);
+ break;
+ case OTG_STATE_A_IDLE:
+ mvotg->otg.default_a = 1;
+ mv_otg_enable(mvotg);
+ if (old_state == OTG_STATE_A_WAIT_VFALL)
+ mv_otg_start_host(mvotg, 0);
+ mv_otg_reset(mvotg);
+ break;
+ case OTG_STATE_A_WAIT_VRISE:
+ mv_otg_set_vbus(&mvotg->otg, 1);
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ if (old_state != OTG_STATE_A_HOST)
+ mv_otg_start_host(mvotg, 1);
+ mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
+ T_A_WAIT_BCON,
+ mv_otg_timer_await_bcon);
+ /*
+ * Now, we directly enter A_HOST. So set b_conn = 1
+ * here. In fact, it need host driver to notify us.
+ */
+ mvotg->otg_ctrl.b_conn = 1;
+ break;
+ case OTG_STATE_A_HOST:
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ /*
+ * Now, we has exited A_HOST. So set b_conn = 0
+ * here. In fact, it need host driver to notify us.
+ */
+ mvotg->otg_ctrl.b_conn = 0;
+ mv_otg_set_vbus(&mvotg->otg, 0);
+ break;
+ case OTG_STATE_A_VBUS_ERR:
+ break;
+ default:
+ break;
+ }
+ goto run;
+ }
+}
+
+static irqreturn_t mv_otg_irq(int irq, void *dev)
+{
+ struct mv_otg *mvotg = dev;
+ u32 otgsc;
+
+ otgsc = readl(&mvotg->op_regs->otgsc);
+ writel(otgsc, &mvotg->op_regs->otgsc);
+
+ /*
+ * if we have vbus, then the vbus detection for B-device
+ * will be done by mv_otg_inputs_irq().
+ */
+ if (mvotg->pdata->vbus)
+ if ((otgsc & OTGSC_STS_USB_ID) &&
+ !(otgsc & OTGSC_INTSTS_USB_ID))
+ return IRQ_NONE;
+
+ if ((otgsc & mvotg->irq_status) == 0)
+ return IRQ_NONE;
+
+ mv_otg_run_state_machine(mvotg, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
+{
+ struct mv_otg *mvotg = dev;
+
+ /* The clock may disabled at this time */
+ if (!mvotg->active) {
+ mv_otg_enable(mvotg);
+ mv_otg_init_irq(mvotg);
+ }
+
+ mv_otg_run_state_machine(mvotg, 0);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t
+get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct mv_otg *mvotg = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ mvotg->otg_ctrl.a_bus_req);
+}
+
+static ssize_t
+set_a_bus_req(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mv_otg *mvotg = dev_get_drvdata(dev);
+
+ if (count > 2)
+ return -1;
+
+ /* We will use this interface to change to A device */
+ if (mvotg->otg.state != OTG_STATE_B_IDLE
+ && mvotg->otg.state != OTG_STATE_A_IDLE)
+ return -1;
+
+ /* The clock may disabled and we need to set irq for ID detected */
+ mv_otg_enable(mvotg);
+ mv_otg_init_irq(mvotg);
+
+ if (buf[0] == '1') {
+ mvotg->otg_ctrl.a_bus_req = 1;
+ mvotg->otg_ctrl.a_bus_drop = 0;
+ dev_dbg(&mvotg->pdev->dev,
+ "User request: a_bus_req = 1\n");
+
+ if (spin_trylock(&mvotg->wq_lock)) {
+ mv_otg_run_state_machine(mvotg, 0);
+ spin_unlock(&mvotg->wq_lock);
+ }
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req,
+ set_a_bus_req);
+
+static ssize_t
+set_a_clr_err(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mv_otg *mvotg = dev_get_drvdata(dev);
+ if (!mvotg->otg.default_a)
+ return -1;
+
+ if (count > 2)
+ return -1;
+
+ if (buf[0] == '1') {
+ mvotg->otg_ctrl.a_clr_err = 1;
+ dev_dbg(&mvotg->pdev->dev,
+ "User request: a_clr_err = 1\n");
+ }
+
+ if (spin_trylock(&mvotg->wq_lock)) {
+ mv_otg_run_state_machine(mvotg, 0);
+ spin_unlock(&mvotg->wq_lock);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
+
+static ssize_t
+get_a_bus_drop(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mv_otg *mvotg = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ mvotg->otg_ctrl.a_bus_drop);
+}
+
+static ssize_t
+set_a_bus_drop(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mv_otg *mvotg = dev_get_drvdata(dev);
+ if (!mvotg->otg.default_a)
+ return -1;
+
+ if (count > 2)
+ return -1;
+
+ if (buf[0] == '0') {
+ mvotg->otg_ctrl.a_bus_drop = 0;
+ dev_dbg(&mvotg->pdev->dev,
+ "User request: a_bus_drop = 0\n");
+ } else if (buf[0] == '1') {
+ mvotg->otg_ctrl.a_bus_drop = 1;
+ mvotg->otg_ctrl.a_bus_req = 0;
+ dev_dbg(&mvotg->pdev->dev,
+ "User request: a_bus_drop = 1\n");
+ dev_dbg(&mvotg->pdev->dev,
+ "User request: and a_bus_req = 0\n");
+ }
+
+ if (spin_trylock(&mvotg->wq_lock)) {
+ mv_otg_run_state_machine(mvotg, 0);
+ spin_unlock(&mvotg->wq_lock);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR,
+ get_a_bus_drop, set_a_bus_drop);
+
+static struct attribute *inputs_attrs[] = {
+ &dev_attr_a_bus_req.attr,
+ &dev_attr_a_clr_err.attr,
+ &dev_attr_a_bus_drop.attr,
+ NULL,
+};
+
+static struct attribute_group inputs_attr_group = {
+ .name = "inputs",
+ .attrs = inputs_attrs,
+};
+
+int mv_otg_remove(struct platform_device *pdev)
+{
+ struct mv_otg *mvotg = platform_get_drvdata(pdev);
+ int clk_i;
+
+ sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group);
+
+ if (mvotg->irq)
+ free_irq(mvotg->irq, mvotg);
+
+ if (mvotg->pdata->vbus)
+ free_irq(mvotg->pdata->vbus->irq, mvotg);
+ if (mvotg->pdata->id)
+ free_irq(mvotg->pdata->id->irq, mvotg);
+
+ if (mvotg->qwork) {
+ flush_workqueue(mvotg->qwork);
+ destroy_workqueue(mvotg->qwork);
+ }
+
+ mv_otg_disable(mvotg);
+
+ if (mvotg->cap_regs)
+ iounmap(mvotg->cap_regs);
+
+ if (mvotg->phy_regs)
+ iounmap(mvotg->phy_regs);
+
+ for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++)
+ clk_put(mvotg->clk[clk_i]);
+
+ otg_set_transceiver(NULL);
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(mvotg);
+
+ return 0;
+}
+
+static int mv_otg_probe(struct platform_device *pdev)
+{
+ struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+ struct mv_otg *mvotg;
+ struct resource *r;
+ int retval = 0, clk_i, i;
+ size_t size;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "failed to get platform data\n");
+ return -ENODEV;
+ }
+
+ size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum;
+ mvotg = kzalloc(size, GFP_KERNEL);
+ if (!mvotg) {
+ dev_err(&pdev->dev, "failed to allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, mvotg);
+
+ mvotg->pdev = pdev;
+ mvotg->pdata = pdata;
+
+ mvotg->clknum = pdata->clknum;
+ for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) {
+ mvotg->clk[clk_i] = clk_get(&pdev->dev, pdata->clkname[clk_i]);
+ if (IS_ERR(mvotg->clk[clk_i])) {
+ retval = PTR_ERR(mvotg->clk[clk_i]);
+ goto err_put_clk;
+ }
+ }
+
+ mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
+ if (!mvotg->qwork) {
+ dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
+ retval = -ENOMEM;
+ goto err_put_clk;
+ }
+
+ INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
+
+ /* OTG common part */
+ mvotg->pdev = pdev;
+ mvotg->otg.dev = &pdev->dev;
+ mvotg->otg.label = driver_name;
+ mvotg->otg.set_host = mv_otg_set_host;
+ mvotg->otg.set_peripheral = mv_otg_set_peripheral;
+ mvotg->otg.set_vbus = mv_otg_set_vbus;
+ mvotg->otg.state = OTG_STATE_UNDEFINED;
+
+ for (i = 0; i < OTG_TIMER_NUM; i++)
+ init_timer(&mvotg->otg_ctrl.timer[i]);
+
+ r = platform_get_resource_byname(mvotg->pdev,
+ IORESOURCE_MEM, "phyregs");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
+ retval = -ENODEV;
+ goto err_destroy_workqueue;
+ }
+
+ mvotg->phy_regs = ioremap(r->start, resource_size(r));
+ if (mvotg->phy_regs == NULL) {
+ dev_err(&pdev->dev, "failed to map phy I/O memory\n");
+ retval = -EFAULT;
+ goto err_destroy_workqueue;
+ }
+
+ r = platform_get_resource_byname(mvotg->pdev,
+ IORESOURCE_MEM, "capregs");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no I/O memory resource defined\n");
+ retval = -ENODEV;
+ goto err_unmap_phyreg;
+ }
+
+ mvotg->cap_regs = ioremap(r->start, resource_size(r));
+ if (mvotg->cap_regs == NULL) {
+ dev_err(&pdev->dev, "failed to map I/O memory\n");
+ retval = -EFAULT;
+ goto err_unmap_phyreg;
+ }
+
+ /* we will acces controller register, so enable the udc controller */
+ retval = mv_otg_enable_internal(mvotg);
+ if (retval) {
+ dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
+ goto err_unmap_capreg;
+ }
+
+ mvotg->op_regs =
+ (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
+ + (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
+
+ if (pdata->id) {
+ retval = request_threaded_irq(pdata->id->irq, NULL,
+ mv_otg_inputs_irq,
+ IRQF_ONESHOT, "id", mvotg);
+ if (retval) {
+ dev_info(&pdev->dev,
+ "Failed to request irq for ID\n");
+ pdata->id = NULL;
+ }
+ }
+
+ if (pdata->vbus) {
+ mvotg->clock_gating = 1;
+ retval = request_threaded_irq(pdata->vbus->irq, NULL,
+ mv_otg_inputs_irq,
+ IRQF_ONESHOT, "vbus", mvotg);
+ if (retval) {
+ dev_info(&pdev->dev,
+ "Failed to request irq for VBUS, "
+ "disable clock gating\n");
+ mvotg->clock_gating = 0;
+ pdata->vbus = NULL;
+ }
+ }
+
+ if (pdata->disable_otg_clock_gating)
+ mvotg->clock_gating = 0;
+
+ mv_otg_reset(mvotg);
+ mv_otg_init_irq(mvotg);
+
+ r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no IRQ resource defined\n");
+ retval = -ENODEV;
+ goto err_disable_clk;
+ }
+
+ mvotg->irq = r->start;
+ if (request_irq(mvotg->irq, mv_otg_irq, IRQF_SHARED,
+ driver_name, mvotg)) {
+ dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
+ mvotg->irq);
+ mvotg->irq = 0;
+ retval = -ENODEV;
+ goto err_disable_clk;
+ }
+
+ retval = otg_set_transceiver(&mvotg->otg);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "can't register transceiver, %d\n",
+ retval);
+ goto err_free_irq;
+ }
+
+ retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group);
+ if (retval < 0) {
+ dev_dbg(&pdev->dev,
+ "Can't register sysfs attr group: %d\n", retval);
+ goto err_set_transceiver;
+ }
+
+ spin_lock_init(&mvotg->wq_lock);
+ if (spin_trylock(&mvotg->wq_lock)) {
+ mv_otg_run_state_machine(mvotg, 2 * HZ);
+ spin_unlock(&mvotg->wq_lock);
+ }
+
+ dev_info(&pdev->dev,
+ "successful probe OTG device %s clock gating.\n",
+ mvotg->clock_gating ? "with" : "without");
+
+ return 0;
+
+err_set_transceiver:
+ otg_set_transceiver(NULL);
+err_free_irq:
+ free_irq(mvotg->irq, mvotg);
+err_disable_clk:
+ if (pdata->vbus)
+ free_irq(pdata->vbus->irq, mvotg);
+ if (pdata->id)
+ free_irq(pdata->id->irq, mvotg);
+ mv_otg_disable_internal(mvotg);
+err_unmap_capreg:
+ iounmap(mvotg->cap_regs);
+err_unmap_phyreg:
+ iounmap(mvotg->phy_regs);
+err_destroy_workqueue:
+ flush_workqueue(mvotg->qwork);
+ destroy_workqueue(mvotg->qwork);
+err_put_clk:
+ for (clk_i--; clk_i >= 0; clk_i--)
+ clk_put(mvotg->clk[clk_i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(mvotg);
+
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mv_otg *mvotg = platform_get_drvdata(pdev);
+
+ if (mvotg->otg.state != OTG_STATE_B_IDLE) {
+ dev_info(&pdev->dev,
+ "OTG state is not B_IDLE, it is %d!\n",
+ mvotg->otg.state);
+ return -EAGAIN;
+ }
+
+ if (!mvotg->clock_gating)
+ mv_otg_disable_internal(mvotg);
+
+ return 0;
+}
+
+static int mv_otg_resume(struct platform_device *pdev)
+{
+ struct mv_otg *mvotg = platform_get_drvdata(pdev);
+ u32 otgsc;
+
+ if (!mvotg->clock_gating) {
+ mv_otg_enable_internal(mvotg);
+
+ otgsc = readl(&mvotg->op_regs->otgsc);
+ otgsc |= mvotg->irq_en;
+ writel(otgsc, &mvotg->op_regs->otgsc);
+
+ if (spin_trylock(&mvotg->wq_lock)) {
+ mv_otg_run_state_machine(mvotg, 0);
+ spin_unlock(&mvotg->wq_lock);
+ }
+ }
+ return 0;
+}
+#endif
+
+static struct platform_driver mv_otg_driver = {
+ .probe = mv_otg_probe,
+ .remove = __exit_p(mv_otg_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = driver_name,
+ },
+#ifdef CONFIG_PM
+ .suspend = mv_otg_suspend,
+ .resume = mv_otg_resume,
+#endif
+};
+
+static int __init mv_otg_init(void)
+{
+ return platform_driver_register(&mv_otg_driver);
+}
+
+static void __exit mv_otg_exit(void)
+{
+ platform_driver_unregister(&mv_otg_driver);
+}
+
+module_init(mv_otg_init);
+module_exit(mv_otg_exit);
diff --git a/drivers/usb/otg/mv_otg.h b/drivers/usb/otg/mv_otg.h
new file mode 100644
index 00000000000..be6ca143764
--- /dev/null
+++ b/drivers/usb/otg/mv_otg.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __MV_USB_OTG_CONTROLLER__
+#define __MV_USB_OTG_CONTROLLER__
+
+#include <linux/types.h>
+
+/* Command Register Bit Masks */
+#define USBCMD_RUN_STOP (0x00000001)
+#define USBCMD_CTRL_RESET (0x00000002)
+
+/* otgsc Register Bit Masks */
+#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001
+#define OTGSC_CTRL_VUSB_CHARGE 0x00000002
+#define OTGSC_CTRL_OTG_TERM 0x00000008
+#define OTGSC_CTRL_DATA_PULSING 0x00000010
+#define OTGSC_STS_USB_ID 0x00000100
+#define OTGSC_STS_A_VBUS_VALID 0x00000200
+#define OTGSC_STS_A_SESSION_VALID 0x00000400
+#define OTGSC_STS_B_SESSION_VALID 0x00000800
+#define OTGSC_STS_B_SESSION_END 0x00001000
+#define OTGSC_STS_1MS_TOGGLE 0x00002000
+#define OTGSC_STS_DATA_PULSING 0x00004000
+#define OTGSC_INTSTS_USB_ID 0x00010000
+#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000
+#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000
+#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000
+#define OTGSC_INTSTS_B_SESSION_END 0x00100000
+#define OTGSC_INTSTS_1MS 0x00200000
+#define OTGSC_INTSTS_DATA_PULSING 0x00400000
+#define OTGSC_INTR_USB_ID 0x01000000
+#define OTGSC_INTR_A_VBUS_VALID 0x02000000
+#define OTGSC_INTR_A_SESSION_VALID 0x04000000
+#define OTGSC_INTR_B_SESSION_VALID 0x08000000
+#define OTGSC_INTR_B_SESSION_END 0x10000000
+#define OTGSC_INTR_1MS_TIMER 0x20000000
+#define OTGSC_INTR_DATA_PULSING 0x40000000
+
+#define CAPLENGTH_MASK (0xff)
+
+/* Timer's interval, unit 10ms */
+#define T_A_WAIT_VRISE 100
+#define T_A_WAIT_BCON 2000
+#define T_A_AIDL_BDIS 100
+#define T_A_BIDL_ADIS 20
+#define T_B_ASE0_BRST 400
+#define T_B_SE0_SRP 300
+#define T_B_SRP_FAIL 2000
+#define T_B_DATA_PLS 10
+#define T_B_SRP_INIT 100
+#define T_A_SRP_RSPNS 10
+#define T_A_DRV_RSM 5
+
+enum otg_function {
+ OTG_B_DEVICE = 0,
+ OTG_A_DEVICE
+};
+
+enum mv_otg_timer {
+ A_WAIT_BCON_TIMER = 0,
+ OTG_TIMER_NUM
+};
+
+/* PXA OTG state machine */
+struct mv_otg_ctrl {
+ /* internal variables */
+ u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */
+ u8 b_srp_done;
+ u8 b_hnp_en;
+
+ /* OTG inputs */
+ u8 a_bus_drop;
+ u8 a_bus_req;
+ u8 a_clr_err;
+ u8 a_bus_resume;
+ u8 a_bus_suspend;
+ u8 a_conn;
+ u8 a_sess_vld;
+ u8 a_srp_det;
+ u8 a_vbus_vld;
+ u8 b_bus_req; /* B-Device Require Bus */
+ u8 b_bus_resume;
+ u8 b_bus_suspend;
+ u8 b_conn;
+ u8 b_se0_srp;
+ u8 b_sess_end;
+ u8 b_sess_vld;
+ u8 id;
+ u8 a_suspend_req;
+
+ /*Timer event */
+ u8 a_aidl_bdis_timeout;
+ u8 b_ase0_brst_timeout;
+ u8 a_bidl_adis_timeout;
+ u8 a_wait_bcon_timeout;
+
+ struct timer_list timer[OTG_TIMER_NUM];
+};
+
+#define VUSBHS_MAX_PORTS 8
+
+struct mv_otg_regs {
+ u32 usbcmd; /* Command register */
+ u32 usbsts; /* Status register */
+ u32 usbintr; /* Interrupt enable */
+ u32 frindex; /* Frame index */
+ u32 reserved1[1];
+ u32 deviceaddr; /* Device Address */
+ u32 eplistaddr; /* Endpoint List Address */
+ u32 ttctrl; /* HOST TT status and control */
+ u32 burstsize; /* Programmable Burst Size */
+ u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
+ u32 reserved[4];
+ u32 epnak; /* Endpoint NAK */
+ u32 epnaken; /* Endpoint NAK Enable */
+ u32 configflag; /* Configured Flag register */
+ u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
+ u32 otgsc;
+ u32 usbmode; /* USB Host/Device mode */
+ u32 epsetupstat; /* Endpoint Setup Status */
+ u32 epprime; /* Endpoint Initialize */
+ u32 epflush; /* Endpoint De-initialize */
+ u32 epstatus; /* Endpoint Status */
+ u32 epcomplete; /* Endpoint Interrupt On Complete */
+ u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
+ u32 mcr; /* Mux Control */
+ u32 isr; /* Interrupt Status */
+ u32 ier; /* Interrupt Enable */
+};
+
+struct mv_otg {
+ struct otg_transceiver otg;
+ struct mv_otg_ctrl otg_ctrl;
+
+ /* base address */
+ void __iomem *phy_regs;
+ void __iomem *cap_regs;
+ struct mv_otg_regs __iomem *op_regs;
+
+ struct platform_device *pdev;
+ int irq;
+ u32 irq_status;
+ u32 irq_en;
+
+ struct delayed_work work;
+ struct workqueue_struct *qwork;
+
+ spinlock_t wq_lock;
+
+ struct mv_usb_platform_data *pdata;
+
+ unsigned int active;
+ unsigned int clock_gating;
+ unsigned int clknum;
+ struct clk *clk[0];
+};
+
+#endif