diff options
author | Imre Kaloz <kaloz@openwrt.org> | 2012-05-01 07:00:17 +0000 |
---|---|---|
committer | Imre Kaloz <kaloz@openwrt.org> | 2012-05-01 07:00:17 +0000 |
commit | 9d2b3add2a5d7fce77952c77332101b53640a3a4 (patch) | |
tree | 7e3c3b52269e3c7564c6a88d67dc4ea6219b041c /target/linux/coldfire/patches/040-Add-USB-support-for-MCF5445x-and-MCF54418.patch | |
parent | 280b7f6d6bcf49ee883d766e666e49394148beb1 (diff) | |
download | master-187ad058-9d2b3add2a5d7fce77952c77332101b53640a3a4.tar.gz master-187ad058-9d2b3add2a5d7fce77952c77332101b53640a3a4.tar.bz2 master-187ad058-9d2b3add2a5d7fce77952c77332101b53640a3a4.zip |
[coldfire]: switch to 2.6.38
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@31546 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/coldfire/patches/040-Add-USB-support-for-MCF5445x-and-MCF54418.patch')
-rw-r--r-- | target/linux/coldfire/patches/040-Add-USB-support-for-MCF5445x-and-MCF54418.patch | 7007 |
1 files changed, 7007 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/040-Add-USB-support-for-MCF5445x-and-MCF54418.patch b/target/linux/coldfire/patches/040-Add-USB-support-for-MCF5445x-and-MCF54418.patch new file mode 100644 index 0000000000..98984be2ff --- /dev/null +++ b/target/linux/coldfire/patches/040-Add-USB-support-for-MCF5445x-and-MCF54418.patch @@ -0,0 +1,7007 @@ +From 632dc9e46a9ff032138f81fd420c0f01fd6c3cbc Mon Sep 17 00:00:00 2001 +From: Jason Jin <Jason.jin@freescale.com> +Date: Thu, 4 Aug 2011 14:21:29 +0800 +Subject: [PATCH 40/52] Add USB support for MCF5445x and MCF54418 + +The OTG module function with external ULPI of USB3300 +on M5445xEVB and Modelo TWR-SER2, while the Max3353 charge +pump on Modelo TWR-SER1 with version-B CPU board only +function FS/LS tranceiver. +It also support USB host module on Modelo TWR-SER2. +And the host mode on M54455EVB with FS/LS only tranceiver +is supported by p4 interface on the board. + +Signed-off-by: Jingchang Lu <b35083@freescale.com> +--- + arch/m68k/Kconfig | 2 + + arch/m68k/coldfire/m5441x/Makefile | 4 +- + arch/m68k/coldfire/m5441x/max3353.h | 96 +++ + arch/m68k/coldfire/m5441x/max3353_otg.c | 508 +++++++++++++ + arch/m68k/coldfire/m5441x/max3353_xc.c | 310 ++++++++ + arch/m68k/coldfire/m5441x/usb.c | 276 +++++++ + arch/m68k/coldfire/m5441x/usb.h | 76 ++ + arch/m68k/coldfire/m5441x/usb_dr.c | 179 +++++ + arch/m68k/coldfire/m5441x/usb_host.c | 115 +++ + arch/m68k/coldfire/m5441x/xcvr.c | 198 +++++ + arch/m68k/coldfire/m5441x/xcvr_host.c | 53 ++ + arch/m68k/coldfire/m5445x/usb.c | 220 ++++++ + arch/m68k/coldfire/m5445x/usb.h | 107 +++ + arch/m68k/coldfire/m5445x/usb_dr.c | 152 ++++ + arch/m68k/coldfire/m5445x/xcvr.c | 127 ++++ + arch/m68k/include/asm/fsl_usb_gadget.h | 45 ++ + arch/m68k/include/asm/fsl_usb_io.h | 43 ++ + arch/m68k/include/asm/fsl_usb_platform.h | 78 ++ + drivers/Makefile | 1 + + drivers/usb/Kconfig | 2 + + drivers/usb/core/Kconfig | 2 +- + drivers/usb/gadget/Kconfig | 2 +- + drivers/usb/gadget/fsl_udc_core.c | 360 ++++++++- + drivers/usb/gadget/fsl_usb2_udc.h | 16 + + drivers/usb/gadget/serial.c | 40 +- + drivers/usb/host/Kconfig | 72 ++- + drivers/usb/host/ehci-fsl.c | 257 ++++++- + drivers/usb/host/ehci-fsl.h | 7 + + drivers/usb/host/ehci-hub.c | 34 + + drivers/usb/host/ehci.h | 55 ++- + drivers/usb/otg/Makefile | 7 + + drivers/usb/otg/fsl_otg.c | 1212 ++++++++++++++++++++++++++++++ + drivers/usb/otg/fsl_otg.h | 423 +++++++++++ + drivers/usb/otg/otg_fsm.c | 371 +++++++++ + drivers/usb/otg/otg_fsm.h | 151 ++++ + drivers/usb/otg/usb.c | 76 ++ + include/linux/fsl_devices.h | 26 + + include/linux/usb/fsl_usb2.h | 410 ++++++++++ + include/linux/usb/fsl_xcvr.h | 36 + + include/linux/usb/otg.h | 2 +- + 40 files changed, 6087 insertions(+), 64 deletions(-) + create mode 100644 arch/m68k/coldfire/m5441x/max3353.h + create mode 100644 arch/m68k/coldfire/m5441x/max3353_otg.c + create mode 100644 arch/m68k/coldfire/m5441x/max3353_xc.c + create mode 100644 arch/m68k/coldfire/m5441x/usb.c + create mode 100644 arch/m68k/coldfire/m5441x/usb.h + create mode 100644 arch/m68k/coldfire/m5441x/usb_dr.c + create mode 100644 arch/m68k/coldfire/m5441x/usb_host.c + create mode 100644 arch/m68k/coldfire/m5441x/xcvr.c + create mode 100644 arch/m68k/coldfire/m5441x/xcvr_host.c + create mode 100644 arch/m68k/coldfire/m5445x/usb.c + create mode 100644 arch/m68k/coldfire/m5445x/usb.h + create mode 100644 arch/m68k/coldfire/m5445x/usb_dr.c + create mode 100644 arch/m68k/coldfire/m5445x/xcvr.c + create mode 100644 arch/m68k/include/asm/fsl_usb_gadget.h + create mode 100644 arch/m68k/include/asm/fsl_usb_io.h + create mode 100644 arch/m68k/include/asm/fsl_usb_platform.h + create mode 100644 drivers/usb/otg/fsl_otg.c + create mode 100644 drivers/usb/otg/fsl_otg.h + create mode 100644 drivers/usb/otg/otg_fsm.c + create mode 100644 drivers/usb/otg/otg_fsm.h + create mode 100644 drivers/usb/otg/usb.c + create mode 100644 include/linux/usb/fsl_usb2.h + create mode 100644 include/linux/usb/fsl_xcvr.h + +--- a/arch/m68k/Kconfig ++++ b/arch/m68k/Kconfig +@@ -327,6 +327,7 @@ config M5445X + bool "MCF5445x support" + depends on COLDFIRE + select GENERIC_TIME ++ select USB_ARCH_HAS_EHCI + select USB_EHCI_FSL + select HAVE_FSL_USB_DR + help +@@ -430,6 +431,7 @@ config M5441X + depends on COLDFIRE + select GENERIC_TIME + select USB_EHCI_FSL ++ select USB_ARCH_HAS_EHCI + select HAVE_FSL_USB_DR + select HAVE_CAN_FLEXCAN + help +--- a/arch/m68k/coldfire/m5441x/Makefile ++++ b/arch/m68k/coldfire/m5441x/Makefile +@@ -14,9 +14,11 @@ endif + endif + + #ifneq ($(strip $(CONFIG_USB) $(CONFIG_USB_GADGET_FSL_USB2)),) +-ifneq ($(strip $(CONFIG_USB_OTG) $(CONFIG_USB_M5441X_MAX3353_FSLS)),) ++ifneq ($(strip $(CONFIG_USB_OTG)),) ++ifeq ($(strip $(CONFIG_USB_M5441X_MAX3353_FSLS)),) + obj-y += xcvr.o + endif ++endif + ifneq ($(strip $(CONFIG_USB_M5441X_MAX3353_FSLS)), ) + obj-y += max3353_xc.o + ifneq ($(strip $(CONFIG_USB_OTG)),) +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/max3353.h +@@ -0,0 +1,96 @@ ++/* ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _MAX3353_H_ ++#define _MAX3353_H_ ++/* registers */ ++ ++/* Manufacturer registers */ ++#define MAX3353_MAN0 0x00 ++#define MAX3353_MAN1 0x01 ++#define MAX3353_MAN2 0x02 ++#define MAX3353_MAN3 0x03 ++ ++/* Product ID registers */ ++#define MAX3353_PID0 0x04 ++#define MAX3353_PID1 0x05 ++#define MAX3353_PID2 0x06 ++#define MAX3353_PID3 0x07 ++ ++/* Control register 1 */ ++#define MAX3353_CR1 0x10 ++#define CR1_DM_PULLDOWN (1 << 7) ++#define CR1_DP_PULLDOWN (1 << 6) ++#define CR1_DM_PULLUP (1 << 5) ++#define CR1_DP_PULLUP (1 << 4) ++#define CR1_BDISC_ACONN (1 << 2) ++#define CR1_IRQ_PUSH_PULL (1 << 1) ++ ++/* Control register 2 */ ++#define MAX3353_CR2 0x11 ++#define CR2_VBUS_DISCHRG (1 << 4) ++#define CR2_VBUS_DRV (1 << 3) ++#define CR2_VBUS_CHG2 (1 << 2) ++#define CR2_VBUS_CHG1 (1 << 1) ++#define CR2_SHUTDOWN (1 << 0) ++ ++/* Status register */ ++#define MAX3353_STATUS 0x13 ++#define STATUS_B_HNP (1 << 6) ++#define STATUS_A_HNP (1 << 5) ++#define STATUS_ID_FLOAT (1 << 4) ++#define STATUS_ID_GND (1 << 3) ++#define STATUS_SESSION_END (1 << 2) ++#define STATUS_SESSION_VALID (1 << 1) ++#define STATUS_VBUS_VALID (1 << 0) ++ ++/* Interrupt mask register */ ++#define MAX3353_INTR_MASK 0x14 ++#define IM_A_HNP_EN (1 << 5) ++#define IM_ID_FLOAT_EN (1 << 4) ++#define IM_ID_GND_EN (1 << 3) ++#define IM_SESSION_END_EN (1 << 2) ++#define IM_SESSION_VLD_EN (1 << 1) ++#define IM_VBUS_VLD_EN (1 << 0) ++ ++/* Interrupt edge register */ ++#define MAX3353_INTR_EDGE 0x15 ++#define IE_SESSION_VALID_ED (1 << 1) ++#define IE_VBUS_VLD_ED (1 << 0) ++ ++/* Interrupt latch register (read-only) */ ++#define MAX3353_INTR_LATCH 0x16 ++#define IL_A_HNP_RQ (1 << 7) ++#define IL_ID_FLOAT_RQ (1 << 6) ++#define IL_ID_GND_RQ (1 << 5) ++#define IL_SESSION_END_RQ (1 << 4) ++#define IL_SESSION_VALID_RN (1 << 3) ++#define IL_VBUS_VALID_RN (1 << 2) ++#define IL_SESSION_VALID_RP (1 << 1) ++#define IL_VBUS_VALID_RP (1 << 0) ++ ++ ++#define MAX3353_DEV_ADDR 0x2c ++#define MAX3353_IRQ (64 + 4) /* IRQ4 */ ++ ++extern u8 max3353_read_reg(u8 reg); ++extern void max3353_write_reg(u8 reg, u8 data); ++extern int max3353_read_id_pin(void); ++extern int otg_set_resources(struct resource *resources); ++ ++#endif /* _MAX3353_H_ */ +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/max3353_otg.c +@@ -0,0 +1,508 @@ ++/* ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/err.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/moduleparam.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <linux/usb.h> ++#include <linux/usb/gadget.h> ++#include <linux/usb/otg.h> ++#include <linux/workqueue.h> ++#include <linux/time.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <linux/usb/fsl_usb2.h> ++#include <linux/usb/otg.h> ++ ++#include <asm/fsl_usb_io.h> ++#include "max3353.h" ++ ++#define DRIVER_VERSION "1.0" ++#define DRIVER_AUTHOR "Freescale Semiconductor Inc." ++#define DRIVER_DESC "Freescale MAX3353 USBOTG Driver" ++#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC ++ ++MODULE_DESCRIPTION("Freescale MAX3353 USBOTG Driver"); ++ ++#if 0 ++#undef pr_debug ++#define pr_debug(x...) printk(x) ++#endif ++ ++static const char driver_name[] = "max3353-otg"; ++ ++const pm_message_t otg_suspend_state = { ++ .event = 1, ++}; ++ ++static volatile struct fsl_usb_device_regs *usb_dr_regs; ++ ++struct otg_fsm { ++ int id; /* 0=host 1=gadget */ ++ struct otg_fsm_ops *ops; ++ struct otg_transceiver *transceiver; ++}; ++ ++struct otg_fsm_ops { ++ int (*start_host)(struct otg_fsm *fsm, int on); ++ int (*start_gadget)(struct otg_fsm *fsm, int on); ++}; ++ ++struct max_otg { ++ struct otg_transceiver otg; ++ struct otg_fsm fsm; ++ struct delayed_work otg_event; ++ struct work_struct work_wq; ++ u8 host_working; ++ int irq; ++}; ++ ++static struct max_otg *max_otg_dev; ++ ++#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) ++ ++ ++/* Reset controller, not reset the bus */ ++void otg_reset_controller(void) ++{ ++ u32 command; ++ ++ command = fsl_readl(&usb_dr_regs->usbcmd); ++ command |= (1 << 1); ++ fsl_writel(command, &usb_dr_regs->usbcmd); ++ while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) ++ ; ++} ++ ++ ++/* Call suspend/resume routines in host driver */ ++static int maxotg_start_host(struct otg_fsm *fsm, int on) ++{ ++ struct otg_transceiver *xceiv = fsm->transceiver; ++ struct device *dev; ++ struct max_otg *otg_dev = container_of(xceiv, struct max_otg, otg); ++ u32 retval = 0; ++ ++ pr_debug("%s: host %s\n", __func__, on ? "on" : "off"); ++ ++ if (!xceiv->host) ++ return -ENODEV; ++ dev = xceiv->host->controller; ++ ++ if (on) { ++ /* start fsl usb host controller */ ++ if (otg_dev->host_working) ++ goto end; ++ else { ++ otg_reset_controller(); ++ pr_debug("host on......\n"); ++ if (dev->driver->resume) ++ retval = dev->driver->resume(dev); ++ ++ otg_dev->host_working = 1; ++ } ++ } else { ++ /* stop fsl usb host controller */ ++ if (!otg_dev->host_working) ++ goto end; ++ else { ++ pr_debug("host off......\n"); ++ if (dev && dev->driver) ++ retval = dev->driver->suspend(dev, ++ otg_suspend_state); ++ otg_dev->host_working = 0; ++ } ++ } ++end: ++ return retval; ++} ++ ++/* Call suspend and resume function in udc driver ++ * to stop and start udc driver. ++ */ ++static int maxotg_start_gadget(struct otg_fsm *fsm, int on) ++{ ++ struct otg_transceiver *xceiv = fsm->transceiver; ++ struct device *dev; ++ ++ pr_debug("%s: gadget %s\n", __func__, on ? "on" : "off"); ++ ++ if (!xceiv->gadget || !xceiv->gadget->dev.parent) ++ return -ENODEV; ++ ++ dev = xceiv->gadget->dev.parent; ++ ++ if (on) ++ dev->driver->resume(dev); ++ else ++ dev->driver->suspend(dev, otg_suspend_state); ++ ++ return 0; ++} ++ ++ ++/* delayed interrupt handler */ ++static void handle_irq(struct work_struct *work) ++{ ++ u8 latch; ++ int id_host; ++ struct otg_fsm *fsm = &max_otg_dev->fsm; ++ struct otg_transceiver *otg = &max_otg_dev->otg; ++ ++ if (max_otg_dev == NULL) ++ return; ++ ++ latch = max3353_read_reg(MAX3353_INTR_LATCH); ++ ++ if (latch) { ++ id_host = max3353_read_id_pin(); ++ fsm->id = !id_host; ++ pr_debug("\t\t\t%s host mode %s\n", __func__, ++ id_host ? "ON" : "OFF"); ++ ++ if (otg->host) ++ otg->host->is_b_host = fsm->id; ++ if (otg->gadget) ++ otg->gadget->is_a_peripheral = !fsm->id; ++ ++ if (fsm->id) { /* switch to gadget */ ++ schedule_delayed_work(&max_otg_dev->otg_event, 100); ++ } else { /* switch to host */ ++ cancel_delayed_work(&max_otg_dev->otg_event); ++ maxotg_start_gadget(fsm, 0); ++ maxotg_start_host(fsm, 1); ++ } ++ } ++ enable_irq(MAX3353_IRQ); ++} ++ ++DECLARE_DELAYED_WORK(max3353_dwq, handle_irq); ++ ++/* ++ * MAX3353 interrupt handler ++ * ++ * We're interested in ID pin changes. Since we can't do i2c ++ * operations from interrupt context, we use a delayed workqueue ++ * routine to clear the interrupt in the MAX3353, and to handle ++ * the role switching. The delay also allows for debouncing the ++ * state of the ID pin. To avoid the flood of interrupts between ++ * the irq and its processing, we disable the irq here, and re-enable ++ * it in the delayed routine. ++ */ ++irqreturn_t max3353_isr(int irq, void *dev_id) ++{ ++ disable_irq(MAX3353_IRQ); ++ schedule_delayed_work(&max3353_dwq, 25); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* Called by initialization code of host driver. Register host controller ++ * to the OTG. Suspend host for OTG role detection. ++ */ ++static int set_host(struct otg_transceiver *otg_p, struct usb_bus *host) ++{ ++ struct max_otg *otg_dev = container_of(otg_p, struct max_otg, otg); ++ ++ pr_debug("%s() host %p\n", __func__, host); ++ ++ if (!otg_p || otg_dev != max_otg_dev) ++ return -ENODEV; ++ ++ otg_p->host = host; ++ if (host) { ++ pr_debug("host off......\n"); ++ ++ /* must leave time for khubd to finish its thing ++ * before yanking the host driver out from under it, ++ * so suspend the host after a short delay. ++ */ ++ otg_dev->host_working = 1; ++ schedule_delayed_work(&otg_dev->otg_event, 100); ++ return 0; ++ } else { /* host driver going away */ ++ if (max3353_read_id_pin()) { ++ /* Mini-A cable connected */ ++ otg_p->state = OTG_STATE_UNDEFINED; ++ } ++ } ++ ++ otg_dev->host_working = 0; ++ ++ return 0; ++} ++ ++/* Called by initialization code of udc. Register udc to OTG.*/ ++static int set_peripheral(struct otg_transceiver *otg_p, ++ struct usb_gadget *gadget) ++{ ++ struct max_otg *otg_dev = container_of(otg_p, struct max_otg, otg); ++ ++ pr_debug("%s gadget %p\n", __func__, gadget); ++ ++ pr_debug("otg_dev 0x%x\n", (int)otg_dev); ++ pr_debug("max_otg_dev 0x%x\n", (int)max_otg_dev); ++ ++ if (!otg_p || otg_dev != max_otg_dev) ++ return -ENODEV; ++ ++ if (!gadget) { ++ if (!otg_dev->otg.default_a) ++ otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0); ++ usb_gadget_vbus_disconnect(otg_dev->otg.gadget); ++ otg_dev->otg.gadget = 0; ++ return 0; ++ } ++ ++ otg_p->gadget = gadget; ++ otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id; ++ ++ /* start the gadget right away if the ID pin says Mini-B */ ++ pr_debug("ID pin=%d\n", otg_dev->fsm.id); ++ if (otg_dev->fsm.id == 1) { ++ maxotg_start_host(&otg_dev->fsm, 0); ++ maxotg_start_gadget(&otg_dev->fsm, 1); ++ } ++ ++ return 0; ++} ++ ++/* Set OTG port power, only for B-device */ ++static int set_power(struct otg_transceiver *otg_p, unsigned mA) ++{ ++ return 0; ++} ++ ++/* Delayed pin detect interrupt processing. ++ * ++ * When the Mini-A cable is disconnected from the board, ++ * the pin-detect interrupt happens before the disconnnect ++ * 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 ++ * by this routine. ++ */ ++static void maxotg_event(struct work_struct *work) ++{ ++ struct max_otg *og = container_of(work, struct max_otg, otg_event.work); ++ struct otg_fsm *fsm = &og->fsm; ++ ++ if (fsm->id) { /* switch to gadget */ ++ maxotg_start_host(fsm, 0); ++ maxotg_start_gadget(fsm, 1); ++ } ++} ++ ++ ++static struct otg_fsm_ops maxotg_ops = { ++ .start_host = maxotg_start_host, ++ .start_gadget = maxotg_start_gadget, ++}; ++ ++ ++/* Initialize the global variable max_otg_dev and request IRQ for OTG */ ++static int maxotg_conf(struct platform_device *pdev) ++{ ++ int status; ++ struct max_otg *max_otg_tc; ++ struct fsl_usb2_platform_data *pdata; ++ ++ pdata = pdev->dev.platform_data; ++ ++ pr_debug("%s\n", __func__); ++ ++ if (max_otg_dev) ++ return 0; ++ ++ /* allocate space to fsl otg device */ ++ max_otg_tc = kzalloc(sizeof(struct max_otg), GFP_KERNEL); ++ if (!max_otg_tc) ++ return -ENODEV; ++ ++ INIT_DELAYED_WORK(&max_otg_tc->otg_event, maxotg_event); ++ ++ /* Set OTG state machine operations */ ++ max_otg_tc->fsm.ops = &maxotg_ops; ++ ++ /* initialize the otg structure */ ++ max_otg_tc->otg.label = DRIVER_DESC; ++ max_otg_tc->otg.set_host = set_host; ++ max_otg_tc->otg.set_peripheral = set_peripheral; ++ max_otg_tc->otg.set_power = set_power; ++ ++ max_otg_dev = max_otg_tc; ++ pr_debug("%s: set max_otg_dev %p, fsm %p\n", __func__, ++ max_otg_dev, &max_otg_dev->fsm); ++ ++ /* Store the otg transceiver */ ++ status = otg_set_transceiver(&max_otg_tc->otg); ++ if (status) { ++ printk(KERN_WARNING ": unable to register OTG transceiver.\n"); ++ return status; ++ } ++ ++ return 0; ++} ++ ++/* OTG Initialization*/ ++static int maxotg_start(struct platform_device *pdev) ++{ ++ struct max_otg *p_otg; ++ struct otg_transceiver *otg_trans = otg_get_transceiver(); ++ struct otg_fsm *fsm; ++ int status; ++ struct resource *res; ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ p_otg = container_of(otg_trans, struct max_otg, otg); ++ fsm = &p_otg->fsm; ++ ++ /* Initialize the state machine structure with default values */ ++ SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); ++ fsm->transceiver = &p_otg->otg; ++ ++ /* We don't require predefined MEM/IRQ resource index */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENXIO; ++ ++ /* ++ * We don't request_mem_region here to enable resource sharing ++ * with host/device ++ */ ++ usb_dr_regs = ioremap(res->start, sizeof(struct fsl_usb_device_regs)); ++ pdata->regs = (void *)usb_dr_regs; ++ pr_debug("ioremap %x to %p\n", res->start, pdata->regs); ++ ++ if (pdata->init && pdata->init(pdev) != 0) ++ return -EINVAL; ++ ++ /* grab the irq */ ++ status = request_irq(MAX3353_IRQ, max3353_isr, IRQF_DISABLED, ++ driver_name, p_otg); ++ if (status) { ++ printk(KERN_ERR "max3353: can't get IRQ, rc %d\n", status); ++ return -EINVAL; ++ } ++ ++ /* enable interrupts on the part */ ++ pr_debug("%s: enabling MAX3353 interrupts\n", __func__); ++ max3353_write_reg(MAX3353_INTR_MASK, IM_ID_GND_EN | IM_ID_FLOAT_EN); ++ ++ /* Export DR controller resources */ ++ otg_set_resources(pdev->resource); ++ ++ /* record initial state of ID pin */ ++ p_otg->fsm.id = !max3353_read_id_pin(); ++ if (p_otg->fsm.id) ++ p_otg->otg.state = OTG_STATE_UNDEFINED; ++ else ++ p_otg->otg.state = OTG_STATE_A_IDLE; ++ ++ pr_debug("initial ID pin: fsm.id=%d (%s)\n", p_otg->fsm.id, ++ p_otg->fsm.id ? "gadget" : "host"); ++ ++ return 0; ++} ++ ++static int __init maxotg_probe(struct platform_device *pdev) ++{ ++ int status; ++ struct fsl_usb2_platform_data *pdata; ++ ++ pr_debug("%s: pdev=0x%p\n", __func__, pdev); ++ ++ if (!pdev) ++ return -ENODEV; ++ ++ if (!pdev->dev.platform_data) ++ return -ENOMEM; ++ ++ pdata = pdev->dev.platform_data; ++ fsl_set_usb_accessors(pdata); ++ ++ /* configure the OTG */ ++ status = maxotg_conf(pdev); ++ if (status) { ++ printk(KERN_INFO "Couldn't init OTG module\n"); ++ return -status; ++ } ++ ++ /* start OTG */ ++ status = maxotg_start(pdev); ++ ++ return status; ++} ++ ++static int __exit maxotg_remove(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ /* disable max3353 interrupts */ ++ max3353_write_reg(MAX3353_INTR_MASK, 0); ++ ++ otg_set_transceiver(NULL); ++ free_irq(MAX3353_IRQ, max_otg_dev); ++ ++ iounmap((void *)usb_dr_regs); ++ ++ kfree(max_otg_dev); ++ max_otg_dev = NULL; ++ ++ if (pdata->exit) ++ pdata->exit(pdev); ++ ++ return 0; ++} ++ ++struct platform_driver max3353_otg_driver = { ++ .probe = maxotg_probe, ++ .remove = __exit_p(maxotg_remove), ++ .driver = { ++ .name = driver_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++static int __init maxotg_init(void) ++{ ++ printk(KERN_INFO DRIVER_DESC " loaded, %s\n", DRIVER_VERSION); ++ return platform_driver_register(&max3353_otg_driver); ++} ++ ++static void __exit maxotg_exit(void) ++{ ++ platform_driver_unregister(&max3353_otg_driver); ++ printk(KERN_INFO DRIVER_DESC " unloaded\n"); ++} ++ ++module_init(maxotg_init); ++module_exit(maxotg_exit); ++ ++MODULE_DESCRIPTION(DRIVER_INFO); ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/max3353_xc.c +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#define DEBUG ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/io.h> ++#include <linux/i2c.h> ++#include <linux/usb/fsl_xcvr.h> ++#include <asm/mcfsim.h> ++#include "max3353.h" ++ ++#define DRIVER_DESC "Freescale MAX3353 Driver" ++ ++static struct i2c_device_id max3353_id[] = { ++ {"max3353", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, max3353_id); ++ ++struct max3353_state { ++ struct i2c_client *client; ++}; ++ ++static int max3353_probe(struct i2c_client *client, ++ const struct i2c_device_id *id); ++static int __devexit max3353_remove(struct i2c_client *client); ++ ++static struct i2c_driver max3353_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "max3353", ++ }, ++ .probe = max3353_probe, ++ .remove = __devexit_p(max3353_remove), ++ .id_table = max3353_id, ++}; ++ ++static struct i2c_client *max3353_i2c_client; ++ ++/* Write a max3353 register*/ ++void max3353_write_reg(u8 reg, u8 data) ++{ ++ i2c_smbus_write_byte_data(max3353_i2c_client, reg, data); ++} ++EXPORT_SYMBOL(max3353_write_reg); ++ ++/* Read a max3353 register*/ ++u8 max3353_read_reg(u8 reg) ++{ ++ return i2c_smbus_read_byte_data(max3353_i2c_client, reg); ++} ++EXPORT_SYMBOL(max3353_read_reg); ++ ++/* ++ * max3353 I2C probe function ++ */ ++ ++static int max3353_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct max3353_state *state; ++ struct device *dev = &client->dev; ++ ++ state = kzalloc(sizeof(struct max3353_state), GFP_KERNEL); ++ if (state == NULL) { ++ dev_err(dev, "failed to create our state\n"); ++ return -ENOMEM; ++ } ++ ++ state->client = client; ++ i2c_set_clientdata(client, state); ++ ++ max3353_i2c_client = client; ++ ++ /* rest of the initialisation goes here. */ ++ printk(KERN_INFO "MAX3353 USB charge pump detected\n"); ++ pr_debug("MANUF:%02x %02x %02x %02x PID:%02x %02x %02x %02x\n", ++ max3353_read_reg(MAX3353_MAN0), ++ max3353_read_reg(MAX3353_MAN1), ++ max3353_read_reg(MAX3353_MAN2), ++ max3353_read_reg(MAX3353_MAN3), ++ max3353_read_reg(MAX3353_PID0), ++ max3353_read_reg(MAX3353_PID1), ++ max3353_read_reg(MAX3353_PID2), ++ max3353_read_reg(MAX3353_PID3)); ++ pr_debug("%s done\n", __func__); ++ ++ return 0; ++ ++} ++ ++/* ++ * max3353 I2C remove function ++ */ ++static int __devexit max3353_remove(struct i2c_client *client) ++{ ++ struct max3353_state *state = i2c_get_clientdata(client); ++ ++ max3353_i2c_client = NULL; ++ ++ kfree(state); ++ return 0; ++} ++ ++static int max3353_running; ++ ++static void max3353_init(struct fsl_xcvr_ops *this) ++{ ++ int __maybe_unused rc; ++ u8 tmp; ++ ++ pr_debug("%s\n", __func__); ++ ++ if (!max3353_running) { ++ i2c_add_driver(&max3353_i2c_driver); ++ ++ /* disable interrupts on the part */ ++ max3353_write_reg(MAX3353_INTR_MASK, 0); ++ ++ /* configure irq mode */ ++ tmp = max3353_read_reg(MAX3353_CR1); ++ tmp |= CR1_IRQ_PUSH_PULL; ++ max3353_write_reg(MAX3353_CR1, tmp); ++ ++ /* take the part out of shutdown */ ++ tmp = max3353_read_reg(MAX3353_CR2); ++ tmp &= ~CR2_SHUTDOWN; /* turn on the part */ ++ max3353_write_reg(MAX3353_CR2, tmp); ++ } ++ max3353_running++; ++#if 0 ++ /* configure INT2 for max3353 INT */ ++ MCF_GPIO_PAR_IRQ0L = ++ (MCF_GPIO_PAR_IRQ0L & MCF_GPIO_PAR_IRQL_IRQ2_MASK) | ++ MCF_GPIO_PAR_IRQL_IRQ2_IRQ2; ++#else ++ /* ++ MCF_GPIO_PAR_IRQ0H |= MCF_GPIO_PAR_IRQH_IRQ7; ++ MCF_EPORT_EPPAR = MCF_EPORT_EPPAR | MCF_EPORT_EPPAR_EPPA7_LEVEL; ++ MCF_EPORT_EPIER = MCF_EPORT_EPIER | MCF_EPORT_EPIER_EPIE7; ++ MCF_INTC0_ICR7 = 2; ++ */ ++ /* use IRQ4 on Twr-Ser1 board by J11 selected */ ++ MCF_GPIO_PAR_IRQ0H |= MCF_GPIO_PAR_IRQH_IRQ4_IRQ4; ++ MCF_EPORT_EPPAR &= ~MCF_EPORT_EPPAR_EPPA4_LEVEL; ++ MCF_EPORT_EPIER = MCF_EPORT_EPIER | MCF_EPORT_EPIER_EPIE4; ++ MCF_INTC0_ICR4 = 7; ++ ++#endif ++ /* Configure USB_DP_PDOWN/USB_DM_PDOWN */ ++ MCF_GPIO_PAR_DBGH1 &= ~MCF_GPIO_PAR_DEBUGH1_DAT0; ++ MCF_GPIO_PAR_DBGH1 &= ~MCF_GPIO_PAR_DEBUGH1_DAT1; ++ MCF_GPIO_PDDR_I |= 0xc0; ++ MCF_GPIO_PODR_I &= 0x3f; ++ ++ /* Enable VBUS_EN and VBUS_OC signals */ ++ MCF_GPIO_PAR_TIMER = ++ (MCF_GPIO_PAR_TIMER & ++ MCF_GPIO_PAR_TIMER_T3IN_MASK) | ++ MCF_GPIO_PAR_TIMER_T3IN_USB0_VBUSEN; ++ ++ MCF_GPIO_PAR_TIMER = ++ (MCF_GPIO_PAR_TIMER & ++ MCF_GPIO_PAR_TIMER_T0IN_MASK) | ++ MCF_GPIO_PAR_TIMER_T0IN_USBO_VBUSOC; ++ ++ /* Setup USB_VBUS_OC signal to be active-low */ ++ MCF_CCM_MISCCR &= (~MCF_CCM_MISCCR_USBOOC); ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_USBPUE; ++} ++ ++static void max3353_uninit(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s\n", __func__); ++ ++ max3353_running--; ++ if (!max3353_running) ++ i2c_del_driver(&max3353_i2c_driver); ++} ++ ++/* set max3353 as USB host */ ++static inline void max3353_set_host(void) ++{ ++ u8 cr1, cr2, status; ++ pr_debug("%s\n", __func__); ++ ++ /* configure transceiver for host mode */ ++ cr1 = max3353_read_reg(MAX3353_CR1); ++ cr2 = max3353_read_reg(MAX3353_CR2); ++ status = max3353_read_reg(MAX3353_STATUS); ++ pr_debug("before: CR1 %02x CR2 %02x STATUS %02x\n", cr1, cr2, status); ++ ++ cr1 &= ~(CR1_DM_PULLDOWN | CR1_DP_PULLDOWN | ++ CR1_DM_PULLUP | CR1_DP_PULLUP); ++ cr1 |= (CR1_DP_PULLDOWN | CR1_DM_PULLDOWN); ++ max3353_write_reg(MAX3353_CR1, cr1); ++ ++ cr1 = max3353_read_reg(MAX3353_CR1); ++ cr2 = max3353_read_reg(MAX3353_CR2); ++ status = max3353_read_reg(MAX3353_STATUS); ++ pr_debug(" after: CR1 %02x CR2 %02x STATUS %02x\n", cr1, cr2, status); ++} ++ ++/* set max3353 as USB device */ ++static inline void max3353_set_dev(void) ++{ ++ u8 cr1; ++ pr_debug("%s\n", __func__); ++ ++ /* FS mode, DP pull up */ ++ cr1 = max3353_read_reg(MAX3353_CR1); ++ cr1 &= ~(CR1_DM_PULLDOWN | CR1_DM_PULLDOWN | CR1_DP_PULLDOWN); ++ cr1 |= CR1_DP_PULLUP; ++ max3353_write_reg(MAX3353_CR1, cr1); ++ ++} ++ ++static void max3353_set_vbus_power(struct fsl_xcvr_ops *this, int on) ++{ ++ pr_debug("%s(on=%d)\n", __func__, on); ++} ++ ++ ++/* Enable or disable the D+ pullup. */ ++static void max3353_pullup(int on) ++{ ++ u8 cr1; ++ unsigned short ccm; ++ ++ pr_debug("%s(%d)\n", __func__, on); ++ ++ cr1 = max3353_read_reg(MAX3353_CR1); ++ cr1 &= ~(CR1_DM_PULLDOWN | CR1_DP_PULLDOWN | ++ CR1_DM_PULLUP | CR1_DP_PULLUP); ++ ++ ccm = MCF_CCM_UOCSR; ++ if (on) { ++ cr1 |= CR1_DP_PULLUP; ++ ccm |= MCF_CCM_UOCSR_BVLD; ++ ccm &= ~MCF_CCM_UOCSR_SEND; ++ } else { ++ ccm &= ~MCF_CCM_UOCSR_BVLD; ++ } ++ ++ max3353_write_reg(MAX3353_CR1, cr1); ++ MCF_CCM_UOCSR = ccm; ++} ++ ++/* ++ * return the state of the ID pin. ++ * return: 0 Mini-B or nothing connected (Gadget mode) ++ * 1 Mini-A connected (Host mode) ++ */ ++int max3353_read_id_pin(void) ++{ ++ u8 status = max3353_read_reg(MAX3353_STATUS); ++ return !!(status & STATUS_ID_GND); ++} ++EXPORT_SYMBOL(max3353_read_id_pin); ++ ++static struct fsl_xcvr_ops max3353_ops = { ++ .name = "dr-max3353", ++ .init = max3353_init, ++ .uninit = max3353_uninit, ++ .set_host = max3353_set_host, ++ .set_device = max3353_set_dev, ++ .set_vbus_power = max3353_set_vbus_power, ++ .pullup = max3353_pullup, ++}; ++ ++/* module init routine. register the transceiver */ ++static int __init max3353xc_init(void) ++{ ++ pr_debug("%s\n", __func__); ++ ++ fsl_usb_xcvr_register(&max3353_ops); ++ return 0; ++} ++ ++static void __exit max3353xc_exit(void) ++{ ++ pr_debug("%s\n", __func__); ++ fsl_usb_xcvr_unregister(&max3353_ops); ++} ++ ++module_init(max3353xc_init); ++module_exit(max3353xc_exit); ++ ++MODULE_ALIAS("max3353"); ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("max3353 transceiver driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/usb.c +@@ -0,0 +1,276 @@ ++/* ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <linux/usb/fsl_xcvr.h> ++#include <asm/mcfsim.h> ++ ++#define MCF_SCM_BCR MCF_REG32(0xFC040024) ++#define MCF_SCM_BCR_GBR (1 << 9) /* global bursts for read */ ++#define MCF_SCM_BCR_GBW (1 << 8) /* global bursts for write */ ++#define MCF_SCM_BCR_SBE_ALL (0xff << 0) /* slave burst enable */ ++ ++#define MAX_XCVR 3 ++struct fsl_xcvr_ops *g_xc_ops[MAX_XCVR] = { NULL }; ++ ++#ifdef ULPI_DEBUG ++void print_ulpi_regs(void) ++{ ++ pr_debug("MCF_SCM_BCR=0x%08lx MCF_CCM_MISCCR=0x%08x " ++ "MCF_CCM_MISSCR2=0x%08x " ++ "MCF_GPIO_PAR_TIMER=0x%08x MCF_GPIO_PAR_FEC=08%08x " ++ "MCF_GPIO_PAR_SIMP0H=08%08x MCF_GPIO_PODR_G=08%08x\n", ++ MCF_SCM_BCR, MCF_CCM_MISCCR, ++ MCF_CCM_MISCCR2, MCF_GPIO_PAR_TIMER, ++ MCF_GPIO_PAR_FEC, MCF_GPIO_PAR_SIMP0H, MCF_GPIO_PODR_G); ++} ++EXPORT_SYMBOL(print_ulpi_regs); ++#endif ++ ++static inline void fsl_usb_enable_clk(void) ++{ ++ pr_debug("%s\n", __func__); ++ ++#if defined(CONFIG_USB_M5441X_MAX3353_FSLS) ++ MCF_PM_PPMCR0 = 0x30; ++ MCF_CCM_MISCCR &= ~(MCF_CCM_MISCCR_USBOOC | MCF_CCM_MISCCR_USBHOC); ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_USBSRC; ++#elif !defined(CONFIG_USB_M5441X_PLLCLK) ++ /* Use external clock source if PLL isn't a multiple of 60MHz */ ++ MCF_CCM_MISCCR &= ~MCF_CCM_MISCCR_USBSRC; ++ ++ /* Initialize the USB Clock: use USB input clock */ ++ MCF_GPIO_PAR_IRQ0L = ++ (MCF_GPIO_PAR_IRQ0L & ++ MCF_GPIO_PAR_IRQL_IRQ6_MASK) | ++ MCF_GPIO_PAR_IRQL_IRQ6_USBCLKIN; ++#else ++ uint fvco, usbclk; ++ ++ fvco = CONFIG_MCFCLK * ((MCF_PLL_DR & MCF_PLL_DR_OUTDIV1) + 1); ++ usbclk = fvco / (((MCF_PLL_DR & MCF_PLL_DR_OUTDIV4) >> 16) + 1); ++ ++ if (usbclk != 60000000) ++ printk(KERN_ERR, "The usb host clock must be 60MHZ!\n\n"); ++ ++ MCF_PM_PPMCR0 = 0x30; ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_USBSRC; ++#endif ++} ++ ++static inline void fsl_usb_disable_clk(void) ++{ ++ pr_debug("%s\n", __func__); ++} ++ ++void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops) ++{ ++ int i; ++ ++ pr_debug("%s '%s'\n", __func__, xcvr_ops->name); ++ for (i = 0; i < MAX_XCVR; i++) { ++ if (g_xc_ops[i] == NULL) { ++ g_xc_ops[i] = xcvr_ops; ++ return; ++ } ++ } ++ ++ pr_debug("%s failed\n", __func__); ++} ++EXPORT_SYMBOL_GPL(fsl_usb_xcvr_register); ++ ++void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops) ++{ ++ int i; ++ ++ pr_debug("%s '%s'\n", __func__, xcvr_ops->name); ++ for (i = 0; i < MAX_XCVR; i++) { ++ if (g_xc_ops[i] == xcvr_ops) { ++ g_xc_ops[i] = NULL; ++ return; ++ } ++ } ++ ++ pr_debug("%s failed\n", __func__); ++} ++EXPORT_SYMBOL_GPL(fsl_usb_xcvr_unregister); ++ ++static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name) ++{ ++ int i; ++ ++ pr_debug("%s '%s'\n", __func__, name); ++ if (name == NULL) { ++ printk(KERN_ERR "get_xcvr(): No tranceiver name\n"); ++ return NULL; ++ } ++ ++ for (i = 0; i < MAX_XCVR; i++) { ++ if (strcmp(g_xc_ops[i]->name, name) == 0) ++ return g_xc_ops[i]; ++ } ++ pr_debug("Failed %s\n", __func__); ++ return NULL; ++} ++ ++/* The dmamask must be set for EHCI to work */ ++static u64 ehci_dmamask = ~(u32) 0; ++ ++/*! ++ * Register an instance of a USB host platform device. ++ * ++ * @param res: resource pointer ++ * @param n_res: number of resources ++ * @param config: config pointer ++ * ++ * @return newly-registered platform_device ++ * ++ * Each supported host interface is registered as an instance ++ * of the "fsl-ehci" device. Call this function multiple times ++ * to register each host interface. ++ */ ++static int instance_id; ++struct platform_device *host_pdev_register(struct resource *res, int n_res, ++ struct fsl_usb2_platform_data *config) ++{ ++ struct platform_device *pdev; ++ int rc; ++ ++ pr_debug("register host res=0x%p, size=%d\n", res, n_res); ++ ++ pdev = platform_device_register_simple("fsl-ehci", ++ instance_id, res, n_res); ++ if (IS_ERR(pdev)) { ++ printk(KERN_ERR "usb: can't register %s Host, %ld\n", ++ config->name, PTR_ERR(pdev)); ++ return NULL; ++ } ++ ++ pdev->dev.coherent_dma_mask = 0xffffffff; ++ pdev->dev.dma_mask = &ehci_dmamask; ++ ++ rc = platform_device_add_data(pdev, config, ++ sizeof(struct fsl_usb2_platform_data)); ++ if (rc) { ++ platform_device_unregister(pdev); ++ return NULL; ++ } ++ ++ printk(KERN_INFO "usb: %s host (%s) registered\n", config->name, ++ config->transceiver); ++ pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n", ++ pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data); ++ ++ instance_id++; ++ ++ return pdev; ++} ++ ++int usb_platform_host_init(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ struct fsl_xcvr_ops *xops; ++ ++ xops = fsl_usb_get_xcvr(pdata->transceiver); ++ if (!xops) { ++ printk(KERN_ERR "DR transceiver ops missing\n"); ++ return -EINVAL; ++ } ++ pdata->xcvr_ops = xops; ++ pdata->xcvr_type = xops->xcvr_type; ++ pdata->pdev = pdev; ++ xops->pdata = pdata; ++ ++ if (pdata->gpio_usb_active) ++ if (pdata->gpio_usb_active()) ++ return -EINVAL; ++ ++ /* enable USB read, write and slave bursts */ ++ MCF_SCM_BCR = MCF_SCM_BCR_GBR | MCF_SCM_BCR_GBW | MCF_SCM_BCR_SBE_ALL; ++ fsl_usb_enable_clk(); ++ ++ if (xops->init) ++ xops->init(xops); ++ ++ return 0; ++} ++ ++void usb_platform_host_uninit(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) ++ pdata->xcvr_ops->uninit(pdata->xcvr_ops); ++ ++ pdata->regs = NULL; ++ ++ if (pdata->gpio_usb_inactive) ++ pdata->gpio_usb_inactive(); ++ ++ fsl_usb_disable_clk(); ++} ++ ++ ++static int dr_used; ++ ++int usb_platform_dr_init(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ struct fsl_xcvr_ops *xops; ++ ++ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata); ++ ++ xops = fsl_usb_get_xcvr(pdata->transceiver); ++ if (!xops) { ++ printk(KERN_ERR "DR transceiver ops missing\n"); ++ return -EINVAL; ++ } ++ pdata->xcvr_ops = xops; ++ pdata->xcvr_type = xops->xcvr_type; ++ pdata->pdev = pdev; ++ xops->pdata = pdata; ++ ++ /* enable USB read, write and slave bursts */ ++ MCF_SCM_BCR = MCF_SCM_BCR_GBR | MCF_SCM_BCR_GBW | MCF_SCM_BCR_SBE_ALL; ++ ++ if (!dr_used) { ++ fsl_usb_enable_clk(); ++ ++ if (xops->init) ++ xops->init(xops); ++ } ++ ++ dr_used++; ++ pr_debug("%s: success\n", __func__); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(usb_platform_dr_init); ++ ++void usb_platform_dr_uninit(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ pr_debug("%s\n", __func__); ++ ++ dr_used--; ++ if (!dr_used) { ++ if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) ++ pdata->xcvr_ops->uninit(pdata->xcvr_ops); ++ ++ pdata->regs = NULL; ++ fsl_usb_disable_clk(); ++ } ++} ++EXPORT_SYMBOL_GPL(usb_platform_dr_uninit); +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/usb.h +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++extern int usb_platform_host_init(struct platform_device *pdev); ++extern void usb_platform_host_uninit(struct fsl_usb2_platform_data *pdata); ++extern int usb_platform_dr_init(struct platform_device *pdev); ++extern void usb_platform_dr_uninit(struct fsl_usb2_platform_data *pdata); ++extern struct platform_device *host_pdev_register(struct resource *res, ++ int n_res, struct fsl_usb2_platform_data *config); ++ ++ ++#if defined CONFIG_USB_EHCI_HCD || defined CONFIG_USB_EHCI_HCD_MODULE ++static inline void dr_register_host(struct resource *r, int rs) ++{ ++ PDATA->operating_mode = DR_HOST_MODE; ++ host_pdev_register(r, rs, PDATA); ++} ++#else ++static inline void dr_register_host(struct resource *r, int rs) ++{ ++} ++#endif ++ ++#ifdef CONFIG_USB_GADGET_FSL_USB2 ++static struct platform_device dr_udc_device; ++ ++static inline void dr_register_udc(void) ++{ ++ PDATA->operating_mode = DR_UDC_MODE; ++ dr_udc_device.dev.platform_data = PDATA; ++ ++ if (platform_device_register(&dr_udc_device)) ++ printk(KERN_ERR "usb: can't register DR gadget\n"); ++ else ++ printk(KERN_INFO "usb: DR gadget (%s) registered\n", ++ PDATA->transceiver); ++} ++#else ++static inline void dr_register_udc(void) ++{ ++} ++#endif ++ ++#ifdef CONFIG_USB_OTG ++static struct platform_device dr_otg_device; ++ ++/* ++ * set the proper operating_mode and ++ * platform_data pointer, then register the ++ * device. ++ */ ++static inline void dr_register_otg(void) ++{ ++ PDATA->operating_mode = FSL_USB2_DR_OTG; ++ dr_otg_device.dev.platform_data = PDATA; ++ ++ if (platform_device_register(&dr_otg_device)) ++ printk(KERN_ERR "usb: can't register otg device\n"); ++ else ++ printk(KERN_INFO "usb: DR OTG registered\n"); ++} ++#else ++static inline void dr_register_otg(void) ++{ ++} ++#endif +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/usb_dr.c +@@ -0,0 +1,179 @@ ++/* ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <asm/mcfsim.h> ++ ++#define USB_OTGREGS_BASE MCF_REG32(0xFC0B0000) ++#define INT_USB (64 + 64 + 64 + 17) /* INTC1:17 17.2.9.1*/ ++#define INT_UOCSR (64 + 64 + 64 + 27) /* INTC1:27 17.2.9.1 */ ++ ++/* ++ * Determine which platform_data struct to use, based on which ++ * transceiver is configured. ++ * PDATA is a pointer to it. ++ */ ++#ifdef CONFIG_USB_M5441X_ULPI ++static struct fsl_usb2_platform_data dr_config_ulpi; ++#define PDATA (&dr_config_ulpi) ++#else ++static struct fsl_usb2_platform_data dr_config_fsls; ++#define PDATA (&dr_config_fsls) ++#endif ++ ++/* ++ * Used to set pdata->operating_mode before registering the platform_device. ++ * If OTG is configured, the controller operates in OTG mode, ++ * otherwise it's either host or device. ++ */ ++#ifdef CONFIG_USB_OTG ++#define DR_UDC_MODE FSL_USB2_DR_OTG ++#define DR_HOST_MODE FSL_USB2_DR_OTG ++#else ++#define DR_UDC_MODE FSL_USB2_DR_DEVICE ++#define DR_HOST_MODE FSL_USB2_DR_HOST ++#endif ++ ++#include "usb.h" ++ ++/* ++ * platform data structs ++ * - Which one to use is determined by CONFIG options in usb.h ++ * - operating_mode plugged at run time ++ */ ++ ++/* off-chip ULPI transceiver */ ++static struct fsl_usb2_platform_data __maybe_unused dr_config_ulpi = { ++ .name = "DR", ++ .init = usb_platform_dr_init, ++ .exit = usb_platform_dr_uninit, ++ .phy_mode = FSL_USB2_PHY_ULPI, ++ .transceiver = "dr-ulpi", ++ .power_budget = 500, ++ .es = 1, ++ .big_endian_mmio = 1, ++ .big_endian_desc = 1, ++ .le_setup_buf = 1, ++}; ++ ++/* on-chip FS/LS serial transceiver */ ++static struct fsl_usb2_platform_data __maybe_unused dr_config_fsls = { ++ .name = "DR", ++ .init = usb_platform_dr_init, ++ .exit = usb_platform_dr_uninit, ++ .phy_mode = FSL_USB2_PHY_SERIAL, ++#ifdef CONFIG_USB_M5441X_MAX3353_FSLS ++ .transceiver = "dr-max3353", ++#else ++ .transceiver = "dr-fsls", ++#endif ++ .power_budget = 500, ++ .es = 1, ++ .big_endian_mmio = 1, ++ .big_endian_desc = 1, ++ .le_setup_buf = 1, ++}; ++ ++/* ++ * resources ++ */ ++static struct resource dr_udc_resources[] = { ++ [0] = { ++ .start = (u32)(&USB_OTGREGS_BASE), ++ .end = (u32)(&USB_OTGREGS_BASE + 0x1ff), ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource dr_otg_resources[] = { ++ [0] = { ++ .start = (u32)(&USB_OTGREGS_BASE), ++ .end = (u32)(&USB_OTGREGS_BASE + 0x1ff), ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource dr_host_resources[] = { ++ [0] = { ++ .start = (u32)(&USB_OTGREGS_BASE), ++ .end = (u32)(&USB_OTGREGS_BASE + 0x1ff), ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 dr_udc_dmamask = ~(u32) 0; ++static void dr_udc_release(struct device *dev) ++{ ++} ++ ++static u64 dr_otg_dmamask = ~(u32) 0; ++static void dr_otg_release(struct device *dev) ++{ ++} ++ ++/* ++ * platform device structs ++ * dev.platform_data field plugged at run time ++ */ ++static struct platform_device __maybe_unused dr_udc_device = { ++ .name = "fsl-usb2-udc", ++ .id = -1, ++ .dev = { ++ .release = dr_udc_release, ++ .dma_mask = &dr_udc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .resource = dr_udc_resources, ++ .num_resources = ARRAY_SIZE(dr_udc_resources), ++}; ++ ++static struct platform_device __maybe_unused dr_otg_device = { ++#ifdef CONFIG_USB_M5441X_MAX3353_FSLS ++ .name = "max3353-otg", ++#else ++ .name = "fsl-usb2-otg", ++#endif ++ .id = -1, ++ .dev = { ++ .release = dr_otg_release, ++ .dma_mask = &dr_otg_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .resource = dr_otg_resources, ++ .num_resources = ARRAY_SIZE(dr_otg_resources), ++}; ++ ++static int __init usb_dr_init(void) ++{ ++ pr_debug("%s:\n", __func__); ++ ++ dr_register_otg(); ++ dr_register_host(dr_host_resources, ARRAY_SIZE(dr_host_resources)); ++ dr_register_udc(); ++ ++ return 0; ++} ++ ++module_init(usb_dr_init); +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/usb_host.c +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <asm/mcfsim.h> ++ ++#define USB_H1REGS_BASE (0xFC0B4000) ++#define INT_USB_HOST (64 + 64 + 64 + 18) /* INTC1:18*/ ++ ++ ++/* ++ * Define Host module platform_data struct to use, based on which ++ * transceiver is configured. ++ * PDATA is a pointer to it. ++ */ ++#if defined(CONFIG_USB_M5441X_H1_FSLS) ++static struct fsl_usb2_platform_data h1_config_fsls; ++#define PDATA (&h1_config_fsls) ++#else ++#warning "Unknow ColdFire USB Host module transceiver!" ++#endif ++/* ++ * Used to set pdata->operating_mode before registering the platform_device. ++ */ ++#define DR_UDC_MODE FSL_USB2_DR_DEVICE ++#define DR_HOST_MODE FSL_USB2_DR_HOST ++ ++#include "usb.h" ++ ++static int h1_gpio_active(void) ++{ ++ /* Configure USB_DP_PDOWN/USB_DM_PDOWN (external resistor) */ ++ MCF_GPIO_PAR_DBGH1 &= ~MCF_GPIO_PAR_DEBUGH1_DAT0; ++ MCF_GPIO_PAR_DBGH1 &= ~MCF_GPIO_PAR_DEBUGH1_DAT1; ++ MCF_GPIO_PDDR_I |= 0xc0; ++ MCF_GPIO_PODR_I &= 0x3f; ++ ++ MCF_GPIO_PAR_CS &= 0x0F; /*Enable GPIOB5, GPIOB6*/ ++ ++ MCF_GPIO_PDDR_B &= 0xDF; /*GPIOB5, input, USB_OC*/ ++ MCF_GPIO_PCR_B |= 0x0C00; /*pull up*/ ++ ++ MCF_GPIO_PDDR_B |= 0x40; /*GPIOB6, output*/ ++ MCF_GPIO_PODR_B |= 0x40; /*GPIOB6, output 1, VBUS enable*/ ++ MCF_GPIO_PCR_B &= ~0x3000; /*pull disable*/ ++ ++ return 0; ++} ++ ++static void h1_gpio_inactive(void) ++{ ++ MCF_GPIO_PODR_B &= 0xBF; /*GPIOB6, output 0*/ ++} ++ ++ ++/* ++ * platform data struct ++ * - operating_mode plugged at run time ++ */ ++ ++/* on-chip FS/LS serial transceiver */ ++static struct fsl_usb2_platform_data usbh1_config = { ++ .name = "Host1", ++ .init = usb_platform_host_init, ++ .exit = usb_platform_host_uninit, ++ .operating_mode = FSL_USB2_MPH_HOST, ++ .power_budget = 500, /* MIC2076: 500 mA max power */ ++ .phy_mode = FSL_USB2_PHY_SERIAL, /* FS/LS on-chip xcvr */ ++ .transceiver = "h1-fsls", ++ .gpio_usb_active = h1_gpio_active, ++ .gpio_usb_inactive = h1_gpio_inactive, ++ .es = 1, ++ .big_endian_mmio = 1, ++ .big_endian_desc = 1, ++}; ++ ++/* ++ * resources ++ */ ++static struct resource h1_resources[] = { ++ [0] = { ++ .start = USB_H1REGS_BASE, ++ .end = USB_H1REGS_BASE + 0x1ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB_HOST, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static int __init usbh1_init(void) ++{ ++ host_pdev_register(h1_resources, ARRAY_SIZE(h1_resources), ++ &usbh1_config); ++ return 0; ++} ++module_init(usbh1_init); +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/xcvr.c +@@ -0,0 +1,198 @@ ++/* ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/delay.h> ++#include <linux/usb/fsl_xcvr.h> ++#include <asm/mcfsim.h> ++ ++/* Use the configured xcvr_ops */ ++#ifdef CONFIG_USB_M5441X_ULPI ++#define XCVR_OPS (&xcvr_ops_ulpi) ++#elif defined CONFIG_USB_M5441X_FSLS ++#define XCVR_OPS (&xcvr_ops_fsls) ++#else ++#error "Invalid USB transceiver selection." ++#endif ++ ++#define MCF_FEC_ENET0_RCR MCF_REG32(0xFC0D4084) ++ ++/* ++ * Init routine for on-chip FSLS xcvr ++ */ ++static void xcvr_fsls_init(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s: name=%s\n", __func__, this->name); ++ ++ /* Configure USB_DP_PDOWN/USB_DM_PDOWN (external resistor) */ ++ MCF_GPIO_PAR_DBGH1 &= ~MCF_GPIO_PAR_DEBUGH1_DAT0; ++ MCF_GPIO_PAR_DBGH1 &= ~MCF_GPIO_PAR_DEBUGH1_DAT1; ++ MCF_GPIO_PDDR_I |= 0xc0; ++ MCF_GPIO_PODR_I &= 0x3f; ++ ++ ++ MCF_GPIO_PAR_CS &= 0x0F; /*Enable GPIOB5, GPIOB6*/ ++ ++ MCF_GPIO_PDDR_B &= 0xDF; /*GPIOB5, input*/ ++ MCF_GPIO_PDDR_B |= 0x40; /*GPIOB6, output*/ ++ ++ MCF_GPIO_PODR_B |= 0x40; /*GPIOB6, output 1*/ ++#if 0 ++ /* Enable VBUS_EN and VBUS_OC signals */ ++ MCF_GPIO_PAR_TIMER = ++ (MCF_GPIO_PAR_TIMER & ++ MCF_GPIO_PAR_TIMER_T3IN_MASK) | ++ MCF_GPIO_PAR_TIMER_T3IN_USB0_VBUSEN; ++ ++ MCF_GPIO_PAR_TIMER = ++ (MCF_GPIO_PAR_TIMER & ++ MCF_GPIO_PAR_TIMER_T0IN_MASK) | ++ MCF_GPIO_PAR_TIMER_T0IN_USBO_VBUSOC; ++ ++ /* Setup USB_VBUS_OC signal to be active-low */ ++ MCF_CCM_MISCCR &= (~MCF_CCM_MISCCR_USBOOC); ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_USBPUE; ++#endif ++ ++} ++ ++/* ++ * Init routine for off-chip ULPI xcvr ++ */ ++static void xcvr_ulpi_init(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s: name=%s\n", __func__, this->name); ++ ++ /* disable FEC2 clock */ ++ MCF_PM_PPMSR0 = 54; ++ ++ /* enable USB OTG clock*/ ++ MCF_PM_PPMCR0 = 44; ++ ++ /* Chip select for ULPI: GPIO G4 as CS between ULPI and RMII1*/ ++ MCF_GPIO_PAR_SIMP0H = ++ (MCF_GPIO_PAR_SIMP0H & ++ MCF_GPIO_PAR_SIMP0H_DAT_MASK) | ++ MCF_GPIO_PAR_SIMP0H_DAT_GPIO; ++ ++ MCF_GPIO_PDDR_G = ++ (MCF_GPIO_PDDR_G & ++ MCF_GPIO_PDDR_G4_MASK) | ++ MCF_GPIO_PDDR_G4_OUTPUT; ++ ++ MCF_GPIO_PODR_G = ++ (MCF_GPIO_PODR_G & ++ MCF_GPIO_PODR_G4_MASK) | ++ MCF_GPIO_PODR_G4_VAL; ++ ++ /* Enable ULPI */ ++ MCF_CCM_MISCCR2 = MCF_CCM_MISCCR2 & (~MCF_CCM_MISCCR2_ULPI); ++ ++ /* Slew rate setting for OTG */ ++ MCF_GPIO_SRCR_FEC = 0x0f; ++ MCF_GPIO_SRCR_TIMER = 0xc3; ++ MCF_GPIO_SRCR_IRQ0 = 0x03; ++ ++ /* Enable the required ULPI signals */ ++ MCF_GPIO_PAR_TIMER = ++ (MCF_GPIO_PAR_TIMER & ++ MCF_GPIO_PAR_TIMER_T3IN_MASK) | ++ MCF_GPIO_PAR_TIMER_T3IN_ULPI_DIR; ++ ++ MCF_GPIO_PAR_TIMER = ++ (MCF_GPIO_PAR_TIMER & ++ MCF_GPIO_PAR_TIMER_T0IN_MASK) | ++ MCF_GPIO_PAR_TIMER_T0IN_ULPI_NXT; ++ ++ MCF_FEC_ENET0_RCR = 0x104; /* FEC0 as RMII mode */ ++ ++ MCF_GPIO_PAR_FEC = ++ (MCF_GPIO_PAR_FEC & ++ MCF_GPIO_PAR_FEC_FEC_MASK) | ++ MCF_GPIO_PAR_FEC_FEC_RMII0FUL_ULPI; ++ ++ ++ pr_debug("MCF_GPIO_PAR_TIMER:0x%x, MCF_GPIO_PAR_FEC:0x%x," ++ "MCF_GPIO_PAR_SIMP0H:0x%x, MCF_GPIO_PDDR_G:0x%x," ++ "MCF_GPIO_PODR_G:0x%x, MCF_CCM_MISCCR2:0x%x\n\n", ++ MCF_GPIO_PAR_TIMER, MCF_GPIO_PAR_FEC, ++ MCF_GPIO_PAR_SIMP0H, MCF_GPIO_PDDR_G, ++ MCF_GPIO_PODR_G, MCF_CCM_MISCCR2); ++ ++ pr_debug("OTGSC:0x%lx, PORTSC:0x%lx, USBMODE:0x%lx, " ++ "USBCMD:0x%lx, USBSTS:0x%lx, USBINTR:0x%lx, " ++ "MCF_CCM_UOCSR:0x%x\n\n", ++ MCF_REG32(0xfc0b01a4), MCF_REG32(0xfc0b0184), ++ MCF_REG32(0xfc0b01a8), MCF_REG32(0xfc0b0140), ++ MCF_REG32(0xfc0b0144), MCF_REG32(0xfc0b0148), ++ MCF_CCM_UOCSR); ++} ++ ++static void xcvr_uninit(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s\n", __func__); ++} ++ ++static void xcvr_pullup(int on) ++{ ++ unsigned short ccm = in_be16(&MCF_CCM_UOCSR); ++ ++ pr_debug("%s(%d)\n", __func__, on); ++ ++ ccm = in_be16(&MCF_CCM_UOCSR); ++ if (on) { ++ ccm |= MCF_CCM_UOCSR_BVLD; ++ ccm &= ~MCF_CCM_UOCSR_SEND; ++ out_be16(&MCF_CCM_UOCSR, ccm); ++ } else { ++ ccm &= ~MCF_CCM_UOCSR_BVLD; ++ out_be16(&MCF_CCM_UOCSR, ccm); ++ } ++} ++ ++struct fsl_xcvr_ops xcvr_ops_ulpi = { ++ .init = xcvr_ulpi_init, ++ .uninit = xcvr_uninit, ++ .pullup = xcvr_pullup, ++ .name = "dr-ulpi", ++}; ++ ++struct fsl_xcvr_ops xcvr_ops_fsls = { ++ .init = xcvr_fsls_init, ++ .uninit = xcvr_uninit, ++ .pullup = xcvr_pullup, ++ .name = "dr-fsls", ++}; ++ ++static int __init usb_xcvr_init(void) ++{ ++ struct fsl_xcvr_ops *xops = XCVR_OPS; ++ ++ pr_debug("%s %s\n", __func__, xops->name); ++ fsl_usb_xcvr_register(xops); ++ ++ return 0; ++} ++ ++static void __exit usb_xcvr_exit(void) ++{ ++ fsl_usb_xcvr_unregister(XCVR_OPS); ++} ++ ++module_init(usb_xcvr_init); ++module_exit(usb_xcvr_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("External ULPI xcvr driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/xcvr_host.c +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/usb/fsl_xcvr.h> ++#include <asm/mcfsim.h> ++ ++/* ++ * xcvr_ops for the on-chip xcvr on the DR controller ++ * No ops needed. ++ */ ++struct fsl_xcvr_ops xcvr_ops_h1_fsls = { ++ .name = "h1-fsls", ++}; ++ ++ ++static int __init usb_xcvr_host_init(void) ++{ ++ fsl_usb_xcvr_register(&xcvr_ops_h1_fsls); ++ ++ return 0; ++} ++ ++static void __exit usb_xcvr_host_exit(void) ++{ ++ fsl_usb_xcvr_unregister(&xcvr_ops_h1_fsls); ++} ++ ++module_init(usb_xcvr_host_init); ++module_exit(usb_xcvr_host_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("On-chip FS/LS xcvr host driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/m68k/coldfire/m5445x/usb.c +@@ -0,0 +1,220 @@ ++/* ++ * Copyright 2004-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <linux/usb/fsl_xcvr.h> ++#include <asm/mcfsim.h> ++ ++#define MCF_SCM_BCR MCF_REG32(0xFC040024) ++#define MCF_SCM_BCR_GBR (1 << 9) /* global bursts for read */ ++#define MCF_SCM_BCR_GBW (1 << 8) /* global bursts for write */ ++#define MCF_SCM_BCR_SBE_ALL (0xff << 0) /* slave burst enable */ ++ ++#define MAX_XCVR 3 ++struct fsl_xcvr_ops *g_xc_ops[MAX_XCVR] = { NULL }; ++ ++#ifdef ULPI_DEBUG ++void print_ulpi_regs(void) ++{ ++ pr_debug("MCF_SCM_BCR=0x%08lx MCF_CCM_MISCCR=0x%08x " ++ "MCF_GPIO_PAR_DMA=0x%08x MCF_GPIO_PAR_USB=08%08x " ++ "MCF_GPIO_PAR_FEC=08%08x\n", ++ MCF_SCM_BCR, MCF_CCM_MISCCR, MCF_GPIO_PAR_DMA, ++ MCF_GPIO_PAR_USB, MCF_GPIO_PAR_FEC); ++} ++EXPORT_SYMBOL(print_ulpi_regs); ++#endif ++ ++static inline void fsl_usb_enable_clk(void) ++{ ++ pr_debug("%s\n", __func__); ++ ++ /* Use external clock source if PLL isn't a multiple of 60MHz */ ++ MCF_CCM_MISCCR &= ~MCF_CCM_MISCCR_USBSRC; ++ ++ /* Initialize the USB Clock: use USB input clock */ ++ MCF_GPIO_PAR_DMA = (MCF_GPIO_PAR_DMA & MCF_GPIO_PAR_DMA_DREQ1_MASK) | ++ MCF_GPIO_PAR_DMA_DREQ1_USB_CLKIN; ++} ++ ++static inline void fsl_usb_disable_clk(void) ++{ ++ pr_debug("%s\n", __func__); ++} ++ ++void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops) ++{ ++ int i; ++ ++ pr_debug("%s '%s'\n", __func__, xcvr_ops->name); ++ for (i = 0; i < MAX_XCVR; i++) { ++ if (g_xc_ops[i] == NULL) { ++ g_xc_ops[i] = xcvr_ops; ++ return; ++ } ++ } ++ ++ pr_debug("%s failed\n", __func__); ++} ++EXPORT_SYMBOL_GPL(fsl_usb_xcvr_register); ++ ++void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops) ++{ ++ int i; ++ ++ pr_debug("%s '%s'\n", __func__, xcvr_ops->name); ++ for (i = 0; i < MAX_XCVR; i++) { ++ if (g_xc_ops[i] == xcvr_ops) { ++ g_xc_ops[i] = NULL; ++ return; ++ } ++ } ++ ++ pr_debug("%s failed\n", __func__); ++} ++EXPORT_SYMBOL_GPL(fsl_usb_xcvr_unregister); ++ ++static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name) ++{ ++ int i; ++ ++ pr_debug("%s '%s'\n", __func__, name); ++ if (name == NULL) { ++ printk(KERN_ERR "get_xcvr(): No tranceiver name\n"); ++ return NULL; ++ } ++ ++ for (i = 0; i < MAX_XCVR; i++) { ++ if (strcmp(g_xc_ops[i]->name, name) == 0) ++ return g_xc_ops[i]; ++ } ++ pr_debug("Failed %s\n", __func__); ++ return NULL; ++} ++ ++/* The dmamask must be set for EHCI to work */ ++static u64 ehci_dmamask = ~(u32) 0; ++ ++/*! ++ * Register an instance of a USB host platform device. ++ * ++ * @param res: resource pointer ++ * @param n_res: number of resources ++ * @param config: config pointer ++ * ++ * @return newly-registered platform_device ++ * ++ * Each supported host interface is registered as an instance ++ * of the "fsl-ehci" device. Call this function multiple times ++ * to register each host interface. ++ */ ++static int instance_id; ++struct platform_device *host_pdev_register(struct resource *res, int n_res, ++ struct fsl_usb2_platform_data *config) ++{ ++ struct platform_device *pdev; ++ int rc; ++ ++ pr_debug("register host res=0x%p, size=%d\n", res, n_res); ++ ++ pdev = platform_device_register_simple("fsl-ehci", ++ instance_id, res, n_res); ++ if (IS_ERR(pdev)) { ++ printk(KERN_ERR "usb: can't register %s Host, %ld\n", ++ config->name, PTR_ERR(pdev)); ++ return NULL; ++ } ++ ++ pdev->dev.coherent_dma_mask = 0xffffffff; ++ pdev->dev.dma_mask = &ehci_dmamask; ++ ++ rc = platform_device_add_data(pdev, config, ++ sizeof(struct fsl_usb2_platform_data)); ++ if (rc) { ++ platform_device_unregister(pdev); ++ return NULL; ++ } ++ ++ printk(KERN_INFO "usb: %s host (%s) registered\n", config->name, ++ config->transceiver); ++ pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n", ++ pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data); ++ ++ instance_id++; ++ ++ return pdev; ++} ++ ++ ++static int dr_used; ++ ++int usb_platform_dr_init(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ struct fsl_xcvr_ops *xops; ++ ++ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata); ++ ++ xops = fsl_usb_get_xcvr(pdata->transceiver); ++ if (!xops) { ++ printk(KERN_ERR "DR transceiver ops missing\n"); ++ return -EINVAL; ++ } ++ pdata->xcvr_ops = xops; ++ pdata->xcvr_type = xops->xcvr_type; ++ pdata->pdev = pdev; ++ xops->pdata = pdata; ++ ++ /* enable USB read, write and slave bursts */ ++ MCF_SCM_BCR = MCF_SCM_BCR_GBR | MCF_SCM_BCR_GBW | MCF_SCM_BCR_SBE_ALL; ++ ++ if (!dr_used) { ++ fsl_usb_enable_clk(); ++ ++ if (xops->init) ++ xops->init(xops); ++ } ++ ++ dr_used++; ++ pr_debug("%s: success\n", __func__); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(usb_platform_dr_init); ++ ++void usb_platform_dr_uninit(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ pr_debug("%s\n", __func__); ++ ++ dr_used--; ++ if (!dr_used) { ++ if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) ++ pdata->xcvr_ops->uninit(pdata->xcvr_ops); ++ ++ pdata->regs = NULL; ++ fsl_usb_disable_clk(); ++ } ++} ++EXPORT_SYMBOL_GPL(usb_platform_dr_uninit); +--- /dev/null ++++ b/arch/m68k/coldfire/m5445x/usb.h +@@ -0,0 +1,107 @@ ++/* ++ * Copyright 2005-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++extern int usb_platform_dr_init(struct platform_device *pdev); ++extern void usb_platform_dr_uninit(struct fsl_usb2_platform_data *pdata); ++extern struct platform_device *host_pdev_register(struct resource *res, ++ int n_res, struct fsl_usb2_platform_data *config); ++ ++ ++/* ++ * Determine which platform_data struct to use, based on which ++ * transceiver is configured. ++ * PDATA is a pointer to it. ++ */ ++#ifdef CONFIG_USB_M5445X_ULPI ++static struct fsl_usb2_platform_data dr_config_ulpi; ++#define PDATA (&dr_config_ulpi) ++#else ++static struct fsl_usb2_platform_data dr_config_fsls; ++#define PDATA (&dr_config_fsls) ++#endif ++ ++/* ++ * Used to set pdata->operating_mode before registering the platform_device. ++ * If OTG is configured, the controller operates in OTG mode, ++ * otherwise it's either host or device. ++ */ ++#ifdef CONFIG_USB_OTG ++#define DR_UDC_MODE FSL_USB2_DR_OTG ++#define DR_HOST_MODE FSL_USB2_DR_OTG ++#else ++#define DR_UDC_MODE FSL_USB2_DR_DEVICE ++#define DR_HOST_MODE FSL_USB2_DR_HOST ++#endif ++ ++ ++#if defined CONFIG_USB_EHCI_HCD || defined CONFIG_USB_EHCI_HCD_MODULE ++static inline void dr_register_host(struct resource *r, int rs) ++{ ++ PDATA->operating_mode = DR_HOST_MODE; ++ host_pdev_register(r, rs, PDATA); ++} ++#else ++static inline void dr_register_host(struct resource *r, int rs) ++{ ++} ++#endif ++ ++#ifdef CONFIG_USB_GADGET_FSL_USB2 ++static struct platform_device dr_udc_device; ++ ++static inline void dr_register_udc(void) ++{ ++ PDATA->operating_mode = DR_UDC_MODE; ++ dr_udc_device.dev.platform_data = PDATA; ++ ++ if (platform_device_register(&dr_udc_device)) ++ printk(KERN_ERR "usb: can't register DR gadget\n"); ++ else ++ printk(KERN_INFO "usb: DR gadget (%s) registered\n", ++ PDATA->transceiver); ++} ++#else ++static inline void dr_register_udc(void) ++{ ++} ++#endif ++ ++#ifdef CONFIG_USB_OTG ++static struct platform_device dr_otg_device; ++ ++/* ++ * set the proper operating_mode and ++ * platform_data pointer, then register the ++ * device. ++ */ ++static inline void dr_register_otg(void) ++{ ++ PDATA->operating_mode = FSL_USB2_DR_OTG; ++ dr_otg_device.dev.platform_data = PDATA; ++ ++ if (platform_device_register(&dr_otg_device)) ++ printk(KERN_ERR "usb: can't register otg device\n"); ++ else ++ printk(KERN_INFO "usb: DR OTG registered\n"); ++} ++#else ++static inline void dr_register_otg(void) ++{ ++} ++#endif +--- /dev/null ++++ b/arch/m68k/coldfire/m5445x/usb_dr.c +@@ -0,0 +1,152 @@ ++/* ++ * Copyright 2004-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <asm/mcfsim.h> ++ ++#define USB_OTGREGS_BASE MCF_REG32(0xFC0B0000) ++#define INT_USB (64 + 64 + 47) /* INTC1:47 16.2.9.1 */ ++#define INT_UOCSR (64 + 64 + 53) /* INTC1:53 16.2.9.1 */ ++ ++#include "usb.h" ++ ++/* ++ * platform data structs ++ * - Which one to use is determined by CONFIG options in usb.h ++ * - operating_mode plugged at run time ++ */ ++ ++/* off-chip ULPI transceiver */ ++static struct fsl_usb2_platform_data __maybe_unused dr_config_ulpi = { ++ .name = "DR", ++ .init = usb_platform_dr_init, ++ .exit = usb_platform_dr_uninit, ++ .phy_mode = FSL_USB2_PHY_ULPI, ++ .transceiver = "dr-ulpi", ++ .power_budget = 500, ++ .es = 1, ++ .big_endian_mmio = 1, ++ .big_endian_desc = 1, ++ .le_setup_buf = 1, ++}; ++ ++/* on-chip FS/LS serial transceiver */ ++static struct fsl_usb2_platform_data __maybe_unused dr_config_fsls = { ++ .name = "DR", ++ .init = usb_platform_dr_init, ++ .exit = usb_platform_dr_uninit, ++ .phy_mode = FSL_USB2_PHY_SERIAL, ++ .transceiver = "dr-fsls", ++ .power_budget = 500, ++ .es = 1, ++ .big_endian_mmio = 1, ++ .big_endian_desc = 1, ++ .le_setup_buf = 1, ++}; ++ ++/* ++ * resources ++ */ ++static struct resource dr_host_resources[] = { ++ [0] = { ++ .start = (u32)(&USB_OTGREGS_BASE), ++ .end = (u32)(&USB_OTGREGS_BASE + 0x1ff), ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource dr_otg_resources[] = { ++ [0] = { ++ .start = (u32)(&USB_OTGREGS_BASE), ++ .end = (u32)(&USB_OTGREGS_BASE + 0x1ff), ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct resource dr_udc_resources[] = { ++ [0] = { ++ .start = (u32)(&USB_OTGREGS_BASE), ++ .end = (u32)(&USB_OTGREGS_BASE + 0x1ff), ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = INT_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++static u64 dr_udc_dmamask = ~(u32) 0; ++static void dr_udc_release(struct device *dev) ++{ ++} ++ ++static u64 dr_otg_dmamask = ~(u32) 0; ++static void dr_otg_release(struct device *dev) ++{ ++} ++ ++/* ++ * platform device structs ++ * dev.platform_data field plugged at run time ++ */ ++static struct platform_device __maybe_unused dr_udc_device = { ++ .name = "fsl-usb2-udc", ++ .id = -1, ++ .dev = { ++ .release = dr_udc_release, ++ .dma_mask = &dr_udc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .resource = dr_udc_resources, ++ .num_resources = ARRAY_SIZE(dr_udc_resources), ++}; ++ ++static struct platform_device __maybe_unused dr_otg_device = { ++ .name = "fsl-usb2-otg", ++ .id = -1, ++ .dev = { ++ .release = dr_otg_release, ++ .dma_mask = &dr_otg_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .resource = dr_otg_resources, ++ .num_resources = ARRAY_SIZE(dr_otg_resources), ++}; ++ ++static int __init usb_dr_init(void) ++{ ++ pr_debug("%s:\n", __func__); ++ ++ dr_register_otg(); ++ dr_register_host(dr_host_resources, ARRAY_SIZE(dr_host_resources)); ++ dr_register_udc(); ++ ++ return 0; ++} ++ ++module_init(usb_dr_init); +--- /dev/null ++++ b/arch/m68k/coldfire/m5445x/xcvr.c +@@ -0,0 +1,127 @@ ++/* ++ * Copyright 2005-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/usb/fsl_xcvr.h> ++#include <asm/mcfsim.h> ++ ++/* Use the configured xcvr_ops */ ++#ifdef CONFIG_USB_M5445X_ULPI ++#define XCVR_OPS (&xcvr_ops_ulpi) ++#elif defined CONFIG_USB_M5445X_FSLS ++#define XCVR_OPS (&xcvr_ops_fsls) ++#else ++#error "Invalid USB transceiver selection." ++#endif ++ ++/* ++ * Init routine for on-chip FSLS xcvr ++ */ ++static void xcvr_fsls_init(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s: name=%s\n", __func__, this->name); ++ ++ /* Enable VBUS_EN and VBUS_OC signals */ ++ MCF_GPIO_PAR_USB = MCF_GPIO_PAR_USB_VBUSEN_VBUSEN | ++ MCF_GPIO_PAR_USB_VBUSOC_VBUSOC; ++ ++ /* Setup USB_VBUS_OC signal to be active-low */ ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_USBOC; ++} ++ ++/* ++ * Init routine for off-chip ULPI xcvr ++ */ ++static void xcvr_ulpi_init(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s: name=%s\n", __func__, this->name); ++ ++ /* Enable the required ULPI signals */ ++ MCF_GPIO_PAR_DMA = (MCF_GPIO_PAR_DMA & ++ MCF_GPIO_PAR_DMA_DACK1_MASK) | ++ MCF_GPIO_PAR_DMA_DACK1_ULPI_DIR; ++ ++ MCF_GPIO_PAR_USB = MCF_GPIO_PAR_USB_VBUSEN_ULPI_NXT | ++ MCF_GPIO_PAR_USB_VBUSOC_ULPI_STP; ++ ++ MCF_GPIO_PAR_FEC = (MCF_GPIO_PAR_FEC & ++ MCF_GPIO_PAR_FEC_FEC0_MASK) | ++ MCF_GPIO_PAR_FEC_FEC0_RMII_ULPI; ++} ++ ++static void xcvr_uninit(struct fsl_xcvr_ops *this) ++{ ++ pr_debug("%s\n", __func__); ++} ++ ++static void xcvr_pullup(int on) ++{ ++ unsigned short ccm = in_be16(&MCF_CCM_UOCSR); ++ ++ pr_debug("%s(%d)\n", __func__, on); ++ ++ ccm = in_be16(&MCF_CCM_UOCSR); ++ if (on) { ++ ccm |= MCF_CCM_UOCSR_BVLD; ++ ccm &= ~MCF_CCM_UOCSR_SEND; ++ out_be16(&MCF_CCM_UOCSR, ccm); ++ } else { ++ ccm &= ~MCF_CCM_UOCSR_BVLD; ++ out_be16(&MCF_CCM_UOCSR, ccm); ++ } ++} ++ ++struct fsl_xcvr_ops xcvr_ops_ulpi = { ++ .init = xcvr_ulpi_init, ++ .uninit = xcvr_uninit, ++ .pullup = xcvr_pullup, ++ .name = "dr-ulpi", ++}; ++ ++struct fsl_xcvr_ops xcvr_ops_fsls = { ++ .init = xcvr_fsls_init, ++ .uninit = xcvr_uninit, ++ .pullup = xcvr_pullup, ++ .name = "dr-fsls", ++}; ++ ++static int __init usb_xcvr_init(void) ++{ ++ struct fsl_xcvr_ops *xops = XCVR_OPS; ++ ++ pr_debug("%s %s\n", __func__, xops->name); ++ fsl_usb_xcvr_register(xops); ++ ++ return 0; ++} ++ ++static void __exit usb_xcvr_exit(void) ++{ ++ fsl_usb_xcvr_unregister(XCVR_OPS); ++} ++ ++module_init(usb_xcvr_init); ++module_exit(usb_xcvr_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("External ULPI xcvr driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/m68k/include/asm/fsl_usb_gadget.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2005-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* ++ * USB Gadget side, platform-specific functionality. ++ */ ++ ++#include <linux/usb/fsl_xcvr.h> ++ ++/* Needed for i2c/serial transceivers */ ++static inline void ++fsl_platform_set_device_mode(struct fsl_usb2_platform_data *pdata) ++{ ++ if (pdata->xcvr_ops && pdata->xcvr_ops->set_device) ++ pdata->xcvr_ops->set_device(); ++} ++ ++static inline void ++fsl_platform_pullup_enable(struct fsl_usb2_platform_data *pdata) ++{ ++ if (pdata->xcvr_ops && pdata->xcvr_ops->pullup) ++ pdata->xcvr_ops->pullup(1); ++} ++ ++static inline void ++fsl_platform_pullup_disable(struct fsl_usb2_platform_data *pdata) ++{ ++ if (pdata->xcvr_ops && pdata->xcvr_ops->pullup) ++ pdata->xcvr_ops->pullup(0); ++} +--- /dev/null ++++ b/arch/m68k/include/asm/fsl_usb_io.h +@@ -0,0 +1,43 @@ ++/* Copyright 2008-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#ifndef _FSL_USB_IO_H ++#define _FSL_USB_IO_H ++ ++#undef inb_p ++#undef outb_p ++#undef inl_p ++#undef outl_p ++#define inb_p in_8 ++#define outb_p(b, addr) (void)((*(__force volatile u8 *) (addr)) = (b)) ++#define inl_p in_be32 ++#define outl_p(l, addr) (void)((*(__force volatile u32 *) (addr)) = (l)) ++ ++#define fsl_readl(addr) in_be32((__force unsigned *)(addr)) ++#define fsl_writel(val32, addr) out_be32((__force unsigned *)(addr), (val32)) ++ ++static inline void fsl_set_usb_accessors(struct fsl_usb2_platform_data *pdata) ++{ ++} ++/* ++#ifndef cpu_to_hc32 ++#define cpu_to_hc32(x) (x) ++#endif ++#ifndef hc32_to_cpu ++#define hc32_to_cpu(x) (x) ++#endif ++*/ ++#endif /* _FSL_USB_IO_H */ +--- /dev/null ++++ b/arch/m68k/include/asm/fsl_usb_platform.h +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef FSL_USB_PLATFORM_H ++#define FSL_USB_PLATFORM_H ++ ++#include <linux/usb/fsl_xcvr.h> ++#include <linux/usb/fsl_usb2.h> ++#include <asm/mcfsim.h> ++ ++extern struct resource *otg_get_resources(void); ++ ++extern struct fsl_usb2_platform_data; ++ ++/* ehci_arc_hc_driver.flags value */ ++#define FSL_PLATFORM_HC_FLAGS (HCD_USB2 | HCD_MEMORY) ++ ++static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, ++ enum fsl_usb2_phy_modes phy_mode, ++ unsigned int port_offset); ++ ++static inline void fsl_platform_usb_setup(struct ehci_hcd *ehci) ++{ ++ struct fsl_usb2_platform_data *pdata; ++ ++ pdata = ehci_to_hcd(ehci)->self.controller->platform_data; ++ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); ++} ++ ++static inline void fsl_platform_set_host_mode(struct usb_hcd *hcd) ++{ ++ unsigned int temp; ++ struct fsl_usb2_platform_data *pdata; ++ struct fsl_usb_host_regs *regs; ++ ++ pdata = hcd->self.controller->platform_data; ++ regs = pdata->regs; ++ ++ if (pdata->xcvr_ops && pdata->xcvr_ops->set_host) ++ pdata->xcvr_ops->set_host(); ++ ++ /* set host mode and select "big endian" */ ++ temp = in_be32(®s->usbmode); ++ temp |= USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); ++ out_be32(®s->usbmode, temp); ++ ++ pr_debug("%s: set usbmode to 0x%x\n\n", __func__, ++ in_be32(®s->usbmode)); ++ ++} ++ ++static inline void ++fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on) ++{ ++#ifdef CONFIG_USB_M5441X_FSLS ++ MCF_GPIO_PAR_CS &= 0x0F; /*Enable GPIOB5, GPIOB6*/ ++ ++ MCF_GPIO_PDDR_B &= 0xDF; /*GPIOB5, input*/ ++ MCF_GPIO_PDDR_B |= 0x40; /*GPIOB6, output*/ ++ ++ if (on) ++ MCF_GPIO_PODR_B |= 0x40; /*GPIOB6, output 1*/ ++ else ++ MCF_GPIO_PODR_B &= 0xBF; /*GPIOB6, output 0*/ ++#endif ++} ++ ++#endif +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -66,6 +66,7 @@ obj-$(CONFIG_PARIDE) += block/paride/ + obj-$(CONFIG_TC) += tc/ + obj-$(CONFIG_UWB) += uwb/ + obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ ++obj-$(CONFIG_USB_OTG) += usb/otg/ + obj-$(CONFIG_USB) += usb/ + obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ + obj-$(CONFIG_PCI) += usb/ +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -57,6 +57,8 @@ config USB_ARCH_HAS_EHCI + boolean + default y if PPC_83xx + default y if PPC_MPC512x ++ default y if M5445X ++ default y if M5441X + default y if SOC_AU1200 + default y if ARCH_IXP4XX + default y if ARCH_W90X900 +--- a/drivers/usb/core/Kconfig ++++ b/drivers/usb/core/Kconfig +@@ -109,7 +109,7 @@ config USB_SUSPEND + config USB_OTG + bool "OTG support" + depends on USB && EXPERIMENTAL +- depends on USB_SUSPEND ++# depends on USB_SUSPEND + default n + help + The most notable feature of USB OTG is support for a +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -156,7 +156,7 @@ config USB_ATMEL_USBA + + config USB_GADGET_FSL_USB2 + boolean "Freescale Highspeed USB DR Peripheral Controller" +- depends on FSL_SOC || ARCH_MXC ++ depends on FSL_SOC || ARCH_MXC || HAVE_FSL_USB_DR + select USB_GADGET_DUALSPEED + select USB_FSL_MPH_DR_OF if OF + help +--- a/drivers/usb/gadget/fsl_udc_core.c ++++ b/drivers/usb/gadget/fsl_udc_core.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. ++ * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Li Yang <leoli@freescale.com> + * Jiang Bo <tanya.jiang@freescale.com> +@@ -52,6 +52,12 @@ + #define DRIVER_AUTHOR "Li Yang/Jiang Bo" + #define DRIVER_VERSION "Apr 20, 2007" + ++ ++#define USB_MODE_CTRL_MODE_MASK 0x00000003 ++#define USB_MODE_ES 0x00000004 /* (big) Endian */ ++#define cpu_to_hc32(x) (x) ++#define hc32_to_cpu(x) (x) ++ + #define DMA_ADDR_INVALID (~(dma_addr_t)0) + + static const char driver_name[] = "fsl-usb2-udc"; +@@ -62,6 +68,9 @@ static struct usb_dr_device *dr_regs; + static struct usb_sys_interface *usb_sys_regs; + #endif + ++static int fsl_udc_suspend(struct device *dev, pm_message_t state); ++static int fsl_udc_resume(struct device *dev); ++ + /* it is initialized in probe() */ + static struct fsl_udc *udc_controller = NULL; + +@@ -76,6 +85,7 @@ fsl_ep0_desc = { + + static void fsl_ep_fifo_flush(struct usb_ep *_ep); + ++#ifndef CONFIG_COLDFIRE + #ifdef CONFIG_PPC32 + #define fsl_readl(addr) in_le32(addr) + #define fsl_writel(val32, addr) out_le32(addr, val32) +@@ -83,7 +93,7 @@ static void fsl_ep_fifo_flush(struct usb + #define fsl_readl(addr) readl(addr) + #define fsl_writel(val32, addr) writel(val32, addr) + #endif +- ++#endif + /******************************************************************** + * Internal Used Function + ********************************************************************/ +@@ -184,6 +194,12 @@ static int dr_controller_setup(struct fs + unsigned long timeout; + #define FSL_UDC_RESET_TIMEOUT 1000 + ++#ifdef CONFIG_COLDFIRE ++ struct fsl_usb2_platform_data *pdata; ++ if (!udc) ++ return -EINVAL; ++ pdata = udc->pdata; ++#endif + /* Config PHY interface */ + portctrl = fsl_readl(&dr_regs->portsc1); + portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); +@@ -226,11 +242,20 @@ static int dr_controller_setup(struct fs + + /* Set the controller as device mode */ + tmp = fsl_readl(&dr_regs->usbmode); ++#ifdef CONFIG_COLDFIRE ++ tmp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */ ++#endif + tmp |= USB_MODE_CTRL_MODE_DEVICE; + /* Disable Setup Lockout */ + tmp |= USB_MODE_SETUP_LOCK_OFF; ++#ifdef CONFIG_COLDFIRE ++ if (pdata->es) ++ tmp |= USB_MODE_ES; ++#endif + fsl_writel(tmp, &dr_regs->usbmode); +- ++#ifdef CONFIG_COLDFIRE ++ fsl_platform_set_device_mode(pdata); ++#endif + /* Clear the setup status */ + fsl_writel(0, &dr_regs->usbsts); + +@@ -243,7 +268,7 @@ static int dr_controller_setup(struct fs + fsl_readl(&dr_regs->endpointlistaddr)); + + /* Config control enable i/o output, cpu endian register */ +-#ifndef CONFIG_ARCH_MXC ++#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_COLDFIRE) + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= USB_CTRL_IOENB; + __raw_writel(ctrl, &usb_sys_regs->control); +@@ -267,7 +292,9 @@ static int dr_controller_setup(struct fs + static void dr_controller_run(struct fsl_udc *udc) + { + u32 temp; +- ++#ifdef CONFIG_COLDFIRE ++ fsl_platform_pullup_enable(udc->pdata); ++#endif + /* Enable DR irq reg */ + temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN + | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN +@@ -277,12 +304,12 @@ static void dr_controller_run(struct fsl + + /* Clear stopped bit */ + udc->stopped = 0; +- ++#ifndef CONFIG_COLDFIRE + /* Set the controller as device mode */ + temp = fsl_readl(&dr_regs->usbmode); + temp |= USB_MODE_CTRL_MODE_DEVICE; + fsl_writel(temp, &dr_regs->usbmode); +- ++#endif + /* Set controller to Run */ + temp = fsl_readl(&dr_regs->usbcmd); + temp |= USB_CMD_RUN_STOP; +@@ -292,7 +319,18 @@ static void dr_controller_run(struct fsl + static void dr_controller_stop(struct fsl_udc *udc) + { + unsigned int tmp; +- ++#ifdef CONFIG_COLDFIRE ++ if (udc->gadget.is_otg) { ++#ifdef CONFIG_USB_M5441X_MAX3353_FSLS ++ if (max3353_read_id_pin()) { ++#else ++ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { ++#endif ++ pr_debug("udc: Leaving early\n"); ++ return; ++ } ++ } ++#endif + /* disable all INTR */ + fsl_writel(0, &dr_regs->usbintr); + +@@ -301,7 +339,9 @@ static void dr_controller_stop(struct fs + + /* disable IO output */ + /* usb_sys_regs->control = 0; */ +- ++#ifdef CONFIG_COLDFIRE ++ fsl_platform_pullup_disable(udc->pdata); ++#endif + /* set controller to Stop */ + tmp = fsl_readl(&dr_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; +@@ -408,10 +448,13 @@ static void struct_ep_qh_setup(struct fs + } + if (zlt) + tmp |= EP_QUEUE_HEAD_ZLT_SEL; +- ++#ifdef CONFIG_COLDFIRE ++ p_QH->max_pkt_length = cpu_to_hc32(tmp); ++#else + p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->next_dtd_ptr = 1; + p_QH->size_ioc_int_sts = 0; ++#endif + } + + /* Setup qh structure and ep register for ep0. */ +@@ -616,7 +659,11 @@ static void fsl_queue_td(struct fsl_ep * + struct fsl_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); + lastreq->tail->next_td_ptr = ++#ifdef CONFIG_COLDFIRE ++ cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); ++#else + cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); ++#endif + /* Read prime bit, if 1 goto done */ + if (fsl_readl(&dr_regs->endpointprime) & bitmask) + goto out; +@@ -641,11 +688,19 @@ static void fsl_queue_td(struct fsl_ep * + + /* Write dQH next pointer and terminate bit to 0 */ + temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; ++#ifdef CONFIG_COLDFIRE ++ dQH->next_dtd_ptr = cpu_to_hc32(temp); ++#else + dQH->next_dtd_ptr = cpu_to_le32(temp); +- ++#endif + /* Clear active and halt bit */ ++#ifdef CONFIG_COLDFIRE ++ temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE ++ | EP_QUEUE_HEAD_STATUS_HALT)); ++#else + temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); ++#endif + dQH->size_ioc_int_sts &= temp; + + /* Ensure that updates to the QH will occure before priming. */ +@@ -682,18 +737,32 @@ static struct ep_td_struct *fsl_build_dt + + dtd->td_dma = *dma; + /* Clear reserved field */ ++#ifdef CONFIG_COLDFIRE ++ swap_temp = hc32_to_cpu(dtd->size_ioc_sts); ++#else + swap_temp = cpu_to_le32(dtd->size_ioc_sts); ++#endif + swap_temp &= ~DTD_RESERVED_FIELDS; ++#ifdef CONFIG_COLDFIRE ++ dtd->size_ioc_sts = cpu_to_hc32(swap_temp); ++#else + dtd->size_ioc_sts = cpu_to_le32(swap_temp); +- ++#endif + /* Init all of buffer page pointers */ + swap_temp = (u32) (req->req.dma + req->req.actual); ++#ifdef CONFIG_COLDFIRE ++ dtd->buff_ptr0 = cpu_to_hc32(swap_temp); ++ dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000); ++ dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000); ++ dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000); ++ dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000); ++#else + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); +- ++#endif + req->req.actual += *length; + + /* zlp is needed if req->req.zero is set */ +@@ -715,9 +784,11 @@ static struct ep_td_struct *fsl_build_dt + /* Enable interrupt for the last dtd of a request */ + if (*is_last && !req->req.no_interrupt) + swap_temp |= DTD_IOC; +- ++#ifdef CONFIG_COLDFIRE ++ dtd->size_ioc_sts = cpu_to_hc32(swap_temp); ++#else + dtd->size_ioc_sts = cpu_to_le32(swap_temp); +- ++#endif + mb(); + + VDBG("length = %d address= 0x%x", *length, (int)*dma); +@@ -743,16 +814,22 @@ static int fsl_req_to_dtd(struct fsl_req + is_first = 0; + req->head = dtd; + } else { ++#ifdef CONFIG_COLDFIRE ++ last_dtd->next_td_ptr = cpu_to_hc32(dma); ++#else + last_dtd->next_td_ptr = cpu_to_le32(dma); ++#endif + last_dtd->next_td_virt = dtd; + } + last_dtd = dtd; + + req->dtd_count++; + } while (!is_last); +- ++#ifdef CONFIG_COLDFIRE ++ dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE); ++#else + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); +- ++#endif + req->tail = dtd; + + return 0; +@@ -963,7 +1040,39 @@ out: + + return status; + } ++#ifdef CONFIG_COLDFIRE ++static int arcotg_fifo_status(struct usb_ep *_ep) ++{ ++ struct fsl_ep *ep; ++ struct fsl_udc *udc; ++ int size = 0; ++ u32 bitmask; ++ struct ep_queue_head *d_qh; ++ ++ ep = container_of(_ep, struct fsl_ep, ep); ++ if (!_ep || (!ep->desc && ep_index(ep) != 0)) ++ return -ENODEV; ++ ++ udc = (struct fsl_udc *)ep->udc; ++ ++ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; ++ ++ bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : ++ (1 << (ep_index(ep))); ++ ++ if (fsl_readl(&dr_regs->endptstatus) & bitmask) ++ size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) ++ >> DTD_LENGTH_BIT_POS; ++ ++ pr_debug("%s %u\n", __func__, size); ++ return size; ++} ++ + ++#endif + static void fsl_ep_fifo_flush(struct usb_ep *_ep) + { + struct fsl_ep *ep; +@@ -1016,6 +1125,9 @@ static struct usb_ep_ops fsl_ep_ops = { + .dequeue = fsl_ep_dequeue, + + .set_halt = fsl_ep_set_halt, ++#ifdef CONFIG_COLDFIRE ++ .fifo_status = arcotg_fifo_status, ++#endif + .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ + }; + +@@ -1396,7 +1508,9 @@ static void tripwire_handler(struct fsl_ + { + u32 temp; + struct ep_queue_head *qh; +- ++#ifdef CONFIG_COLDFIRE ++ struct fsl_usb2_platform_data *pdata = udc->pdata; ++#endif + qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; + + /* Clear bit in ENDPTSETUPSTAT */ +@@ -1410,7 +1524,20 @@ static void tripwire_handler(struct fsl_ + fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); + + /* Copy the setup packet to local buffer */ ++#if CONFIG_COLDFIRE ++ if (pdata->le_setup_buf) { ++ u32 *p = (u32 *)buffer_ptr; ++ u32 *s = (u32 *)qh->setup_buffer; ++ ++ /* Convert little endian setup buffer to CPU endian */ ++ *p++ = le32_to_cpu(*s++); ++ *p = le32_to_cpu(*s); ++ } else { ++ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); ++ } ++#else + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); ++#endif + } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); + + /* Clear Setup Tripwire */ +@@ -1434,19 +1561,19 @@ static int process_ep_req(struct fsl_udc + actual = curr_req->req.length; + + for (j = 0; j < curr_req->dtd_count; j++) { +- remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) ++ remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts) + & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + actual -= remaining_length; + +- if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & +- DTD_ERROR_MASK)) { ++ errors = hc32_to_cpu(curr_td->size_ioc_sts); ++ if (errors & DTD_ERROR_MASK) { + if (errors & DTD_STATUS_HALTED) { + ERR("dTD error %08x QH=%d\n", errors, pipe); + /* Clear the errors and Halt condition */ +- tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); ++ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); + tmp &= ~errors; +- curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); ++ curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp); + status = -EPIPE; + /* FIXME: continue with next queued TD? */ + +@@ -1464,7 +1591,7 @@ static int process_ep_req(struct fsl_udc + ERR("Unknown error has occured (0x%x)!\n", + errors); + +- } else if (le32_to_cpu(curr_td->size_ioc_sts) ++ } else if (hc32_to_cpu(curr_td->size_ioc_sts) + & DTD_STATUS_ACTIVE) { + VDBG("Request not complete"); + status = REQ_UNCOMPLETE; +@@ -1552,7 +1679,10 @@ static void dtd_complete_irq(struct fsl_ + static void port_change_irq(struct fsl_udc *udc) + { + u32 speed; +- ++#ifdef CONFIG_COLDFIRE ++ if (udc->bus_reset) ++ udc->bus_reset = 0; ++#endif + /* Bus resetting is finished */ + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + /* Get the speed */ +@@ -1660,6 +1790,10 @@ static void reset_irq(struct fsl_udc *ud + + if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { + VDBG("Bus reset"); ++#ifdef CONFIG_COLDFIRE ++ /* Bus is reseting */ ++ udc->bus_reset = 1; ++#endif + /* Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + reset_queues(udc); +@@ -1793,19 +1927,49 @@ int usb_gadget_probe_driver(struct usb_g + udc_controller->driver = NULL; + goto out; + } +- ++#ifdef CONFIG_COLDFIRE ++ if (udc_controller->transceiver) { ++ /* Suspend the controller until OTG enable it */ ++ udc_controller->stopped = 1; ++ printk(KERN_INFO "Suspend udc for OTG auto detect\n"); ++ ++ /* export udc suspend/resume call to OTG */ ++ udc_controller->gadget.dev.driver->suspend = fsl_udc_suspend; ++ udc_controller->gadget.dev.driver->resume = fsl_udc_resume; ++ ++ /* connect to bus through transceiver */ ++ if (udc_controller->transceiver) { ++ retval = otg_set_peripheral(udc_controller->transceiver, ++ &udc_controller->gadget); ++ if (retval < 0) { ++ ERR("can't bind to transceiver\n"); ++ driver->unbind(&udc_controller->gadget); ++ udc_controller->gadget.dev.driver = 0; ++ udc_controller->driver = 0; ++ return retval; ++ } ++ } ++ } else { ++ /* Enable DR IRQ reg and Set usbcmd reg Run bit */ ++ dr_controller_run(udc_controller); ++ udc_controller->usb_state = USB_STATE_ATTACHED; ++ udc_controller->ep0_state = WAIT_FOR_SETUP; ++ udc_controller->ep0_dir = 0; ++ } ++#else + /* Enable DR IRQ reg and Set usbcmd reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; ++#endif + printk(KERN_INFO "%s: bind to driver %s\n", + udc_controller->gadget.name, driver->driver.name); + + out: + if (retval) + printk(KERN_WARNING "gadget driver register failed %d\n", +- retval); ++ retval); + return retval; + } + EXPORT_SYMBOL(usb_gadget_probe_driver); +@@ -2239,7 +2403,7 @@ static int __init fsl_udc_probe(struct p + int ret = -ENODEV; + unsigned int i; + u32 dccparams; +- ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + if (strcmp(pdev->name, driver_name)) { + VDBG("Wrong device"); + return -ENODEV; +@@ -2253,6 +2417,24 @@ static int __init fsl_udc_probe(struct p + + spin_lock_init(&udc_controller->lock); + udc_controller->stopped = 1; ++#ifdef CONFIG_COLDFIRE ++ udc_controller->pdata = pdata; ++#endif ++#ifdef CONFIG_USB_OTG ++ /* Memory and interrupt resources will be passed from OTG */ ++ udc_controller->transceiver = otg_get_transceiver(); ++ if (!udc_controller->transceiver) { ++ printk(KERN_ERR "Can't find OTG driver!\n"); ++ ret = -ENODEV; ++ goto err_kfree; ++ } ++ ++ res = otg_get_resources(); ++ if (!res) { ++ DBG("resource not registered!\n"); ++ return -ENODEV; ++ } ++#else + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { +@@ -2266,18 +2448,30 @@ static int __init fsl_udc_probe(struct p + ret = -EBUSY; + goto err_kfree; + } +- ++#endif + dr_regs = ioremap(res->start, resource_size(res)); + if (!dr_regs) { + ret = -ENOMEM; + goto err_release_mem_region; + } ++#ifdef CONFIG_COLDFIRE ++ pdata->regs = (void *)dr_regs; ++ fsl_set_usb_accessors(pdata); ++ ++ /* ++ * do platform specific init: check the clock, grab/config pins, etc. ++ */ ++ if (pdata->init && pdata->init(pdev)) { ++ ret = -ENODEV; ++ goto err_iounmap_noclk; ++ } + ++#else + #ifndef CONFIG_ARCH_MXC + usb_sys_regs = (struct usb_sys_interface *) + ((u32)dr_regs + USB_DR_SYS_OFFSET); + #endif +- ++#endif + /* Initialize USB clocks */ + ret = fsl_udc_clk_init(pdev); + if (ret < 0) +@@ -2293,8 +2487,12 @@ static int __init fsl_udc_probe(struct p + /* Get max device endpoints */ + /* DEN is bidirectional ep number, max_ep doubles the number */ + udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; +- ++#ifdef CONFIG_USB_OTG ++ res++; ++ udc_controller->irq = res->start; ++#else + udc_controller->irq = platform_get_irq(pdev, 0); ++#endif + if (!udc_controller->irq) { + ret = -ENODEV; + goto err_iounmap; +@@ -2314,11 +2512,17 @@ static int __init fsl_udc_probe(struct p + ret = -ENOMEM; + goto err_free_irq; + } +- ++#ifdef CONFIG_COLDFIRE ++ if (!udc_controller->transceiver) { ++ /* initialize usb hw reg except for regs for EP, ++ * leave usbintr reg untouched */ ++ dr_controller_setup(udc_controller); ++ } ++#else + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + dr_controller_setup(udc_controller); +- ++#endif + fsl_udc_clk_finalize(pdev); + + /* Setup gadget structure */ +@@ -2336,7 +2540,10 @@ static int __init fsl_udc_probe(struct p + ret = device_register(&udc_controller->gadget.dev); + if (ret < 0) + goto err_free_irq; +- ++#ifdef CONFIG_COLDFIRE ++ if (udc_controller->transceiver) ++ udc_controller->gadget.is_otg = 1; ++#endif + /* setup QH and epctrl for ep0 */ + ep0_setup(udc_controller); + +@@ -2392,7 +2599,9 @@ err_kfree: + static int __exit fsl_udc_remove(struct platform_device *pdev) + { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- ++#ifdef CONFIG_COLDFIRE ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++#endif + DECLARE_COMPLETION(done); + + if (!udc_controller) +@@ -2412,31 +2621,103 @@ static int __exit fsl_udc_remove(struct + dma_pool_destroy(udc_controller->td_pool); + free_irq(udc_controller->irq, udc_controller); + iounmap(dr_regs); ++#ifndef CONFIG_USB_OTG + release_mem_region(res->start, res->end - res->start + 1); +- ++#endif + device_unregister(&udc_controller->gadget.dev); + /* free udc --wait for the release() finished */ + wait_for_completion(&done); ++#ifdef CONFIG_COLDFIRE ++ /* ++ * do platform specific un-initialization: ++ * release iomux pins, etc. ++ */ ++ if (pdata->exit) ++ pdata->exit(pdev); ++ ++#endif ++ return 0; ++} ++#ifdef CONFIG_COLDFIRE ++ ++static int udc_suspend(struct fsl_udc *udc) ++{ ++ u32 mode, usbcmd; ++ ++ mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK; ++ usbcmd = fsl_readl(&dr_regs->usbcmd); ++ ++ pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped); ++ ++ /* ++ * If the controller is already stopped, then this must be a ++ * PM suspend. Remember this fact, so that we will leave the ++ * controller stopped at PM resume time. ++ */ ++ if (udc->stopped) { ++ pr_debug("gadget already stopped, leaving early\n"); ++ udc->already_stopped = 1; ++ return 0; ++ } + ++ if (mode != USB_MODE_CTRL_MODE_DEVICE) { ++ pr_debug("gadget not in device mode, leaving early\n"); ++ return 0; ++ } ++ ++ printk(KERN_INFO "USB Gadget suspended\n"); ++ ++ /* stop the controller */ ++ usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP; ++ fsl_writel(usbcmd, &dr_regs->usbcmd); ++ ++ udc->stopped = 1; + return 0; + } + ++#endif + /*----------------------------------------------------------------- + * Modify Power management attributes + * Used by OTG statemachine to disable gadget temporarily + -----------------------------------------------------------------*/ ++#ifdef CONFIG_COLDFIRE ++static int fsl_udc_suspend(struct device *dev, pm_message_t state) ++#else + static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) ++#endif + { ++#ifdef CONFIG_COLDFIRE ++ return udc_suspend(udc_controller); ++#else + dr_controller_stop(udc_controller); + return 0; ++#endif + } + + /*----------------------------------------------------------------- + * Invoked on USB resume. May be called in_interrupt. + * Here we start the DR controller and enable the irq + *-----------------------------------------------------------------*/ ++#ifdef CONFIG_COLDFIRE ++static int fsl_udc_resume(struct device *dev) ++#else + static int fsl_udc_resume(struct platform_device *pdev) ++#endif + { ++#ifdef CONFIG_COLDFIRE ++ pr_debug("%s(): stopped %d already_stopped %d\n", __func__, ++ udc_controller->stopped, udc_controller->already_stopped); ++ ++ /* ++ * If the controller was stopped at suspend time, then ++ * don't resume it now. ++ */ ++ if (udc_controller->already_stopped) { ++ udc_controller->already_stopped = 0; ++ pr_debug("gadget was already stopped, leaving early\n"); ++ return 0; ++ } ++#endif + /* Enable DR irq reg and set controller Run */ + if (udc_controller->stopped) { + dr_controller_setup(udc_controller); +@@ -2445,6 +2726,9 @@ static int fsl_udc_resume(struct platfor + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; ++ ++ printk(KERN_INFO "USB Gadget resumed\n"); ++ + return 0; + } + +@@ -2455,11 +2739,15 @@ static int fsl_udc_resume(struct platfor + static struct platform_driver udc_driver = { + .remove = __exit_p(fsl_udc_remove), + /* these suspend and resume are not usb suspend and resume */ ++#ifndef CONFIG_COLDFIRE + .suspend = fsl_udc_suspend, + .resume = fsl_udc_resume, ++#endif + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, ++ .suspend = fsl_udc_suspend, ++ .resume = fsl_udc_resume, + }, + }; + +--- a/drivers/usb/gadget/fsl_usb2_udc.h ++++ b/drivers/usb/gadget/fsl_usb2_udc.h +@@ -461,6 +461,7 @@ struct fsl_ep { + struct fsl_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; ++ struct fsl_usb2_platform_data *pdata; + struct completion *done; /* to make sure release() is done */ + struct fsl_ep *eps; + unsigned int max_ep; +@@ -473,6 +474,7 @@ struct fsl_udc { + unsigned vbus_active:1; + unsigned stopped:1; + unsigned remote_wakeup:1; ++ unsigned already_stopped:1; + + struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ + struct fsl_req *status_req; /* ep0 status request */ +@@ -482,6 +484,7 @@ struct fsl_udc { + size_t ep_qh_size; /* size after alignment adjustment*/ + dma_addr_t ep_qh_dma; /* dma address of QH */ + ++ u32 bus_reset; /* Device is bus reseting */ + u32 max_pipes; /* Device max pipes */ + u32 resume_state; /* USB state to resume */ + u32 usb_state; /* USB current state */ +@@ -581,4 +584,17 @@ static inline void fsl_udc_clk_release(v + } + #endif + ++#ifdef CONFIG_COLDFIRE ++#include <asm/mcfsim.h> ++#include <asm/fsl_usb_io.h> ++#include <asm/fsl_usb_gadget.h> ++#include <asm/cf_cacheflush.h> ++ ++extern struct resource *otg_get_resources(void); ++#ifdef CONFIG_USB_M5441X_MAX3353_FSLS ++extern int max3353_read_id_pin(void); ++#endif ++ ++#endif ++ + #endif +--- a/drivers/usb/gadget/serial.c ++++ b/drivers/usb/gadget/serial.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell + * Copyright (C) 2008 by Nokia Corporation ++ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, +@@ -134,7 +135,44 @@ MODULE_PARM_DESC(use_obex, "Use CDC OBEX + static unsigned n_ports = 1; + module_param(n_ports, uint, 0); + MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); +- ++/*-------------------------------------------------------------------------*/ ++#if 0 ++static void gs_setup_complete_set_line_coding(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct gs_dev *dev = ep->driver_data; ++ struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ ++ ++ switch (req->status) { ++ case 0: ++ /* normal completion */ ++ if (req->actual != sizeof(port->port_line_coding)) ++ usb_ep_set_halt(ep); ++ else if (port) { ++ struct usb_cdc_line_coding *value = req->buf; ++ ++ /* REVISIT: we currently just remember this data. ++ * If we change that, (a) validate it first, then ++ * (b) update whatever hardware needs updating. ++ */ ++ spin_lock(&port->port_lock); ++ port->port_line_coding = *value; ++ spin_unlock(&port->port_lock); ++ } ++ break; ++ ++ case -ESHUTDOWN: ++ /* disconnect */ ++ gs_free_req(ep, req); ++ break; ++ ++ default: ++ /* unexpected */ ++ break; ++ } ++ return; ++} ++#endif + /*-------------------------------------------------------------------------*/ + + static int __init serial_bind_config(struct usb_configuration *c) +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -95,13 +95,14 @@ config USB_EHCI_BIG_ENDIAN_MMIO + bool + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ + ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ +- PPC_MPC512x || CPU_CAVIUM_OCTEON) ++ PPC_MPC512x || CPU_CAVIUM_OCTEON || \ ++ COLDFIRE) + default y + + config USB_EHCI_BIG_ENDIAN_DESC + bool + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ +- PPC_MPC512x) ++ PPC_MPC512x || COLDFIRE) + default y + + config XPS_USB_HCD_XILINX +@@ -120,12 +121,77 @@ config USB_FSL_MPH_DR_OF + + config USB_EHCI_FSL + bool "Support for Freescale on-chip EHCI USB controller" +- depends on USB_EHCI_HCD && FSL_SOC ++ depends on USB_EHCI_HCD && (FSL_SOC || COLDFIRE) + select USB_EHCI_ROOT_HUB_TT + select USB_FSL_MPH_DR_OF if OF + ---help--- + Variation of ARC USB block used in some Freescale chips. + ++config USB_M5441X_PLLCLK ++ bool "ColdFire USB module use PLL clock source" ++ depends on M5441X ++ ---help--- ++ Select USB clock source from PLL instead external USBCLKIN, ++ to generate the exact USB module 60MHZ clock, the system clock ++ must be shrink to match the divide requirement which is down ++ in u-boot. ++ ++config USB_M5441X_H1 ++ bool "ColdFire EHCI USB Host module support" ++ depends on USB_EHCI_HCD && M5441X ++ ---help--- ++ Some ColdFire platform have two USB controllers, one is HOST module, ++ the other is OTG module, this config select the HOST module support. ++ ++choice ++ prompt "Selcet ColdFire HOST module transceiver" ++ depends on USB_M5441X_H1 ++ default USB_M5441X_H1_FSLS ++ ++config USB_M5441X_H1_FSLS ++ bool "on-chip (FS/LS only)" ++ depends on M5441X ++ ---help--- ++ Enable support for the on-chip FS/LS transceiver. ++endchoice ++ ++choice ++ prompt "Select ColdiFire OTG module transceiver" ++ depends on M5445X || M5441X ++ default USB_M5445X_ULPI if M5445X ++ default USB_M5441X_ULPI if M5441X ++ ++config USB_M5445X_ULPI ++ bool "External ULPI" ++ depends on M5445X ++ ---help--- ++ Enable support for the external HS ULPI transceiver. ++ ++config USB_M5445X_FSLS ++ bool "On-chip (FL/LS only)" ++ depends on M54455EVB ++ ---help--- ++ Enable support for the on-chip FL/LS transceiver. ++ ++config USB_M5441X_ULPI ++ bool "External ULPI" ++ depends on M5441X ++ ---help--- ++ Enable support for the external HS ULPI transceiver. ++ ++config USB_M5441X_FSLS ++ bool "On-chip (FL/LS only)" ++ depends on M54418EVB ++ ---help--- ++ Enable support for the on-chip FL/LS transceiver. ++ ++config USB_M5441X_MAX3353_FSLS ++ bool "MAX3353 chip charge pump to on-chip (FS/LS only)" ++ depends on M54418EVB ++ ---help--- ++ Max3353 provides charge pump for SER1 board on m54418 platform. ++endchoice ++ + config USB_EHCI_MXC + bool "Support for Freescale on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_MXC +--- a/drivers/usb/host/ehci-fsl.c ++++ b/drivers/usb/host/ehci-fsl.c +@@ -22,16 +22,21 @@ + * Jerry Huang <Chang-Ming.Huang@freescale.com> and + * Anton Vorontsov <avorontsov@ru.mvista.com>. + */ +- + #include <linux/kernel.h> + #include <linux/types.h> + #include <linux/delay.h> + #include <linux/pm.h> + #include <linux/platform_device.h> + #include <linux/fsl_devices.h> +- ++#include <linux/usb/otg.h> + #include "ehci-fsl.h" + ++#ifdef DEBUG ++#define DBG(X...) printk(X) ++#else ++#define DBG(X...) ++#endif ++ + /* configure so an HC device and id are always provided */ + /* always called with process context; sleeping is OK */ + +@@ -75,7 +80,27 @@ static int usb_hcd_fsl_probe(const struc + dev_name(&pdev->dev)); + return -ENODEV; + } +- ++ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto err1; ++ } ++#ifdef CONFIG_USB_OTG ++ if (pdata->operating_mode == FSL_USB2_DR_OTG) { ++ res = otg_get_resources(); ++ if (!res) { ++ dev_err(&pdev->dev, ++ "Found HC with no IRQ. Check %s setup!\n", ++ dev_name(&pdev->dev)); ++ return -ENODEV; ++ goto err2; ++ } ++ irq = res[1].start; ++ hcd->rsrc_start = res[0].start; ++ hcd->rsrc_len = res[0].end - res[0].start + 1; ++ } else ++#endif ++ { + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, +@@ -85,12 +110,6 @@ static int usb_hcd_fsl_probe(const struc + } + irq = res->start; + +- hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); +- if (!hcd) { +- retval = -ENOMEM; +- goto err1; +- } +- + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, +@@ -101,12 +120,15 @@ static int usb_hcd_fsl_probe(const struc + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; ++#ifndef CONFIG_USB_OTG + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } ++#endif ++ } + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + + if (hcd->regs == NULL) { +@@ -124,22 +146,56 @@ static int usb_hcd_fsl_probe(const struc + retval = -ENODEV; + goto err3; + } +- ++#ifndef CONFIG_COLDFIRE + /* Enable USB controller, 83xx or 8536 */ + if (pdata->have_sysif_regs) + setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4); +- ++#endif + /* Don't need to set host mode here. It will be done by tdi_reset() */ +- ++#ifdef CONFIG_COLDFIRE ++ fsl_platform_set_host_mode(hcd); ++ hcd->power_budget = pdata->power_budget; ++#endif + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err4; ++#ifdef CONFIG_COLDFIRE ++ fsl_platform_set_vbus_power(pdata, 1); ++#endif ++#ifdef CONFIG_USB_OTG ++ if (pdata->operating_mode == FSL_USB2_DR_OTG) { ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ ++ DBG("pdev=0x%p hcd=0x%p ehci=0x%p\n", pdev, hcd, ehci); ++ ++ ehci->transceiver = otg_get_transceiver(); ++ DBG("ehci->transceiver=0x%p\n", ehci->transceiver); ++ ++ if (ehci->transceiver) { ++ retval = otg_set_host(ehci->transceiver, ++ &ehci_to_hcd(ehci)->self); ++ if (retval) { ++ printk(KERN_INFO "otg transceiver set host failed\n"); ++ if (ehci->transceiver) ++ put_device(ehci->transceiver->dev); ++ goto err4; ++ } ++ } else { ++ printk(KERN_ERR "can't find transceiver\n"); ++ retval = -ENODEV; ++ goto err4; ++ } ++ } ++#endif ++ + return retval; + + err4: + iounmap(hcd->regs); + err3: ++#ifndef CONFIG_USB_OTG + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++#endif + err2: + usb_put_hcd(hcd); + err1: +@@ -164,9 +220,24 @@ static void usb_hcd_fsl_remove(struct us + struct platform_device *pdev) + { + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++#ifdef CONFIG_COLDFIRE ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); + ++ /* DDD shouldn't we turn off the power here? */ ++ fsl_platform_set_vbus_power(pdata, 0); ++ ++ if (ehci->transceiver) { ++ (void)otg_set_host(ehci->transceiver, 0); ++ put_device(ehci->transceiver->dev); ++ } else { ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ } ++ ++ ++#endif + usb_remove_hcd(hcd); + ++ usb_put_hcd(hcd); + /* + * do platform specific un-initialization: + * release iomux pins, disable clock, etc. +@@ -174,8 +245,6 @@ static void usb_hcd_fsl_remove(struct us + if (pdata->exit) + pdata->exit(pdev); + iounmap(hcd->regs); +- release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +- usb_put_hcd(hcd); + } + + static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, +@@ -238,7 +307,7 @@ static void ehci_fsl_usb_setup(struct eh + if ((pdata->operating_mode == FSL_USB2_DR_HOST) || + (pdata->operating_mode == FSL_USB2_DR_OTG)) + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); +- ++#ifndef CONFIG_COLDFIRE + if (pdata->operating_mode == FSL_USB2_MPH_HOST) { + unsigned int chip, rev, svr; + +@@ -255,7 +324,7 @@ static void ehci_fsl_usb_setup(struct eh + if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1); + } +- ++#endif + if (pdata->have_sysif_regs) { + #ifdef CONFIG_PPC_85xx + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); +@@ -271,7 +340,11 @@ static void ehci_fsl_usb_setup(struct eh + /* called after powerup, by probe or system-pm "wakeup" */ + static int ehci_fsl_reinit(struct ehci_hcd *ehci) + { ++#ifdef CONFIG_COLDFIRE ++ fsl_platform_usb_setup(ehci); ++#else + ehci_fsl_usb_setup(ehci); ++#endif + ehci_port_power(ehci, 0); + + return 0; +@@ -334,7 +407,6 @@ static struct ehci_fsl *hcd_to_ehci_fsl( + + return container_of(ehci, struct ehci_fsl, ehci); + } +- + static int ehci_fsl_drv_suspend(struct device *dev) + { + struct usb_hcd *hcd = dev_get_drvdata(dev); +@@ -371,7 +443,6 @@ static int ehci_fsl_drv_resume(struct de + + return 0; + } +- + static int ehci_fsl_drv_restore(struct device *dev) + { + struct usb_hcd *hcd = dev_get_drvdata(dev); +@@ -387,6 +458,144 @@ static struct dev_pm_ops ehci_fsl_pm_ops + }; + + #define EHCI_FSL_PM_OPS (&ehci_fsl_pm_ops) ++ ++/* suspend/resume, section 4.3 */ ++ ++/* These routines rely on the bus (pci, platform, etc) ++ * to handle powerdown and wakeup, and currently also on ++ * transceivers that don't need any software attention to set up ++ * the right sort of wakeup. ++ * ++ * They're also used for turning on/off the port when doing OTG. ++ */ ++static int ehci_fsl_suspend(struct device *dev, ++ pm_message_t message) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ u32 tmp; ++ struct fsl_usb2_platform_data *pdata = dev->platform_data; ++ ++#ifdef DEBUG ++ u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); ++ mode &= USBMODE_CM_MASK; ++ tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ ++ ++ DBG(KERN_DEBUG "%s('%s'): suspend=%d already_suspended=%d " ++ "mode=%d usbcmd %08x\n", __func__, pdata->name, ++ pdata->suspended, pdata->already_suspended, mode, tmp); ++#endif ++ /* ++ * If the controller is already suspended, then this must be a ++ * PM suspend. Remember this fact, so that we will leave the ++ * controller suspended at PM resume time. ++ */ ++ if (pdata->suspended) { ++ pr_debug("%s: already suspended, leaving early\n", __func__); ++ pdata->already_suspended = 1; ++ return 0; ++ } ++ ++ pr_debug("%s: suspending...\n", __func__); ++ ++ DBG(KERN_INFO "USB Host suspended\n"); ++ ++ hcd->state = HC_STATE_SUSPENDED; ++ dev->power.power_state = PMSG_SUSPEND; ++ ++ /* ignore non-host interrupts */ ++ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ++ ++ /* stop the controller */ ++ tmp = ehci_readl(ehci, &ehci->regs->command); ++ tmp &= ~CMD_RUN; ++ ehci_writel(ehci, tmp, &ehci->regs->command); ++ ++ /* save EHCI registers */ ++ pdata->pm_command = ehci_readl(ehci, &ehci->regs->command); ++ pdata->pm_command &= ~CMD_RUN; ++ pdata->pm_status = ehci_readl(ehci, &ehci->regs->status); ++ pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); ++ pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); ++ pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment); ++ pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); ++ pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); ++ pdata->pm_configured_flag = ++ ehci_readl(ehci, &ehci->regs->configured_flag); ++ pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); ++ ++ /* clear the W1C bits */ ++ pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS); ++ ++ pdata->suspended = 1; ++#if 0 ++ /* clear PP to cut power to the port */ ++ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); ++ tmp &= ~PORT_POWER; ++ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); ++#endif ++ return 0; ++} ++ ++static int ehci_fsl_resume(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ u32 tmp; ++ struct fsl_usb2_platform_data *pdata = dev->platform_data; ++ ++ pr_debug("%s('%s'): suspend=%d already_suspended=%d\n", __func__, ++ pdata->name, pdata->suspended, pdata->already_suspended); ++ ++ /* ++ * If the controller was already suspended at suspend time, ++ * then don't resume it now. ++ */ ++ if (pdata->already_suspended) { ++ pr_debug("already suspended, leaving early\n"); ++ pdata->already_suspended = 0; ++ return 0; ++ } ++ ++ if (!pdata->suspended) { ++ pr_debug("not suspended, leaving early\n"); ++ return 0; ++ } ++ ++ DBG(KERN_INFO "USB Host resumed\n"); ++ ++ pdata->suspended = 0; ++ ++ pr_debug("%s resuming...\n", __func__); ++ ++ /* set host mode */ ++ fsl_platform_set_host_mode(hcd); ++ ++ ++ /* restore EHCI registers */ ++ ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); ++ ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable); ++ ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index); ++ ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment); ++ ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list); ++ ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next); ++ ehci_writel(ehci, pdata->pm_configured_flag, ++ &ehci->regs->configured_flag); ++ ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); ++ ++ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ++ hcd->state = HC_STATE_RUNNING; ++ dev->power.power_state = PMSG_ON; ++ ++ tmp = ehci_readl(ehci, &ehci->regs->command); ++ tmp |= CMD_RUN; ++ ehci_writel(ehci, tmp, &ehci->regs->command); ++ ++ usb_hcd_resume_root_hub(hcd); ++ ++ return 0; ++} ++ + #else + #define EHCI_FSL_PM_OPS NULL + #endif /* CONFIG_PM */ +@@ -431,7 +640,11 @@ static const struct hc_driver ehci_fsl_h + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, ++#ifndef CONFIG_COLDFIRE + .port_handed_over = ehci_port_handed_over, ++#else ++ .start_port_reset = ehci_start_port_reset, ++#endif + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + }; +@@ -460,8 +673,16 @@ static struct platform_driver ehci_fsl_d + .probe = ehci_fsl_drv_probe, + .remove = ehci_fsl_drv_remove, + .shutdown = usb_hcd_platform_shutdown, ++#ifdef CONFIG_PM ++#ifndef CONFIG_COLDFIRE ++ .suspend = ehci_fsl_suspend, ++ .resume = ehci_fsl_resume, ++#endif ++#endif + .driver = { + .name = "fsl-ehci", + .pm = EHCI_FSL_PM_OPS, ++ .suspend = ehci_fsl_suspend, ++ .resume = ehci_fsl_resume, + }, + }; +--- a/drivers/usb/host/ehci-fsl.h ++++ b/drivers/usb/host/ehci-fsl.h +@@ -42,4 +42,11 @@ + #define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ + #define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ + #define SNOOP_SIZE_2GB 0x1e ++ ++#ifdef CONFIG_COLDFIRE ++#define FSL_SOC_USB_USBMODE 0x1a8 ++ ++#include <asm/fsl_usb_platform.h> ++#include <asm/fsl_usb_io.h> ++#endif + #endif /* _EHCI_FSL_H */ +--- a/drivers/usb/host/ehci-hub.c ++++ b/drivers/usb/host/ehci-hub.c +@@ -1,5 +1,6 @@ + /* + * Copyright (C) 2001-2004 by David Brownell ++ * Copyright (C) 2011 Freescale Semiconductor, Inc. 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 +@@ -41,6 +42,39 @@ static int ehci_hub_control( + u16 wLength + ); + ++#ifdef CONFIG_COLDFIRE ++ ++#ifdef CONFIG_USB_OTG ++static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) ++{ ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ u32 status; ++ ++ if (!port) ++ return -EINVAL; ++ port--; ++ ++ /* start port reset before HNP protocol time out */ ++ status = readl(&ehci->regs->port_status[port]); ++ if (!(status & PORT_CONNECT)) ++ return -ENODEV; ++ ++ /* khubd will finish the reset later */ ++ if (ehci_is_TDI(ehci)) ++ writel(PORT_RESET | (status & ~(PORT_CSC | PORT_PEC ++ | PORT_OCC)), &ehci->regs->port_status[port]); ++ else ++ writel(PORT_RESET, &ehci->regs->port_status[port]); ++ ++ return 0; ++} ++#else ++static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) ++{ ++ return 0; ++} ++#endif /* CONFIG_USB_OTG */ ++#endif + /* After a power loss, ports that were owned by the companion must be + * reset so that the companion can still own them. + */ +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -1,6 +1,7 @@ + /* + * Copyright (c) 2001-2002 by David Brownell +- * ++ * Copyright (C) 2011 Freescale Semiconductor, Inc. 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 +@@ -147,7 +148,9 @@ struct ehci_hcd { /* one per controlle + unsigned has_lpm:1; /* support link power management */ + unsigned has_ppcd:1; /* support per-port change bits */ + u8 sbrn; /* packed release number */ +- ++#if defined(CONFIG_COLDFIRE) ++ struct otg_transceiver *transceiver; ++#endif + /* irq statistics */ + #ifdef EHCI_STATS + struct ehci_stats stats; +@@ -617,6 +620,54 @@ ehci_port_speed(struct ehci_hcd *ehci, u + #define writel_be(val, addr) __raw_writel(val, (__force unsigned *)addr) + #endif + ++#if defined(CONFIG_COLDFIRE) ++ ++#if defined(CONFIG_MMU) ++#include <asm/fsl_usb_io.h> ++/* ++ * Sorry, include/asm-m68k/io.h is messed up. It will give you a ++ * BE readl or a LE readl depending on whether or not CONFIG_PCI is set. ++ * how broken is that? Do the right thing here. ++ */ ++#undef readl ++#undef writel ++ ++#define readl(addr) in_le32((__force unsigned *)(addr)) ++#define writel(val, addr) out_le32((__force unsigned *)(addr), (val)) ++ ++#define readl_be(addr) in_be32((__force unsigned *)(addr)) ++#define writel_be(val, addr) out_be32((__force unsigned *)(addr), (val)) ++ ++#else /* !CONFIG_MMU */ ++ ++#define readl_be(addr) \ ++ ({u32 __v = (*(__force volatile u32 *)addr); __v; }) ++#define writel_be(val, addr) \ ++ (void)((*(__force volatile u32 *)addr) = val) ++ ++#ifndef in_be32 ++#define in_be32(addr) \ ++ ({ u32 __v = (*(__force volatile u32 *)(addr)); __v; }) ++#endif ++ ++#ifndef in_le32 ++#define in_le32(addr) \ ++ ({ u32 __v = le32_to_cpu(*(__force volatile __le32 *)(addr)); __v; }) ++#endif ++ ++#ifndef out_be32 ++#define out_be32(addr, l) (void)((*(__force volatile u32 *)(addr)) = (l)) ++#endif ++ ++#ifndef out_le32 ++#define out_le32(addr, l) \ ++ (void)((*(__force volatile __le32 *)(addr)) = cpu_to_le32(l)) ++#endif ++ ++#endif ++ ++#endif /* CONFIG_COLDFIRE */ ++ + static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, + __u32 __iomem * regs) + { +--- a/drivers/usb/otg/Makefile ++++ b/drivers/usb/otg/Makefile +@@ -18,3 +18,10 @@ obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-x + obj-$(CONFIG_USB_ULPI) += ulpi.o + obj-$(CONFIG_USB_MSM_OTG_72K) += msm72k_otg.o + obj-$(CONFIG_AB8500_USB) += ab8500-usb.o ++ ++obj-$(CONFIG_USB_OTG) += usb.o ++fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o ++#ifneq ($(CONFIG_USB_OTG),) ++ifneq ($(CONFIG_USB_OTG),) ++obj-m += fsl_usb2_otg.o ++endif +--- /dev/null ++++ b/drivers/usb/otg/fsl_otg.c +@@ -0,0 +1,1212 @@ ++/* ++ * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Author: Li Yang <LeoLi@freescale.com> ++ * Jerry Huang <Chang-Ming.Huang@freescale.com> ++ * ++ * Initialization based on code from Shlomi Gridish. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/smp_lock.h> ++#include <linux/proc_fs.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/reboot.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/usb.h> ++#include <linux/device.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++#include <linux/workqueue.h> ++#include <linux/time.h> ++#include <linux/fsl_devices.h> ++#include <linux/platform_device.h> ++ ++#include <linux/io.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/byteorder.h> ++#include <linux/uaccess.h> ++#include <asm/unaligned.h> ++ ++#include "fsl_otg.h" ++ ++/* ++#undef DBG ++#undef VDBG ++#define DBG(x...) printk(x) ++#define VDBG(x...) printk(x) ++*/ ++ ++#define CONFIG_USB_OTG_DEBUG_FILES ++#define DRIVER_VERSION "$Revision: 1.55 $" ++#define DRIVER_AUTHOR "Jerry Huang/Li Yang" ++#define DRIVER_DESC "Freescale USB OTG Driver" ++#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC ++ ++MODULE_DESCRIPTION("Freescale USB OTG Transceiver Driver"); ++ ++static const char driver_name[] = "fsl-usb2-otg"; ++ ++const pm_message_t otg_suspend_state = { ++ .event = 1, ++}; ++ ++#define HA_DATA_PULSE 1 ++ ++volatile static struct usb_dr_mmap *usb_dr_regs; ++static struct fsl_otg *fsl_otg_dev; ++static int srp_wait_done; ++ ++/* FSM timers */ ++struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, ++ *b_ase0_brst_tmr, *b_se0_srp_tmr; ++ ++/* Driver specific timers */ ++struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, ++ *b_srp_wait_tmr, *a_wait_enum_tmr; ++ ++static struct list_head active_timers; ++ ++static struct fsl_otg_config fsl_otg_initdata = { ++ .otg_port = 1, ++}; ++ ++/* Routines to access transceiver ULPI registers */ ++u8 view_ulpi(u8 addr) ++{ ++ u32 temp; ++ ++ temp = 0x40000000 | (addr << 16); ++ temp = cpu_to_le32(temp); ++ fsl_writel(temp, &usb_dr_regs->ulpiview); ++ udelay(1000); ++ while (temp & 0x40) ++ temp = fsl_readl(&usb_dr_regs->ulpiview); ++ return (le32_to_cpu(temp) & 0x0000ff00) >> 8; ++} ++ ++int write_ulpi(u8 addr, u8 data) ++{ ++ u32 temp; ++ ++ temp = 0x60000000 | (addr << 16) | data; ++ temp = cpu_to_hc32(temp); ++ fsl_writel(temp, &usb_dr_regs->ulpiview); ++ return 0; ++} ++ ++/* -------------------------------------------------------------*/ ++/* Operations that will be called from OTG Finite State Machine */ ++ ++/* Charge vbus for vbus pulsing in SRP */ ++void fsl_otg_chrg_vbus(int on) ++{ ++ u32 tmp; ++ ++ tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; ++ ++ if (on) ++ /* stop discharging, start charging */ ++ tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | ++ OTGSC_CTRL_VBUS_CHARGE; ++ else ++ /* stop charging */ ++ tmp &= ~OTGSC_CTRL_VBUS_CHARGE; ++ ++ fsl_writel(tmp, &usb_dr_regs->otgsc); ++} ++ ++/* Discharge vbus through a resistor to ground */ ++void fsl_otg_dischrg_vbus(int on) ++{ ++ u32 tmp; ++ ++ tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; ++ ++ if (on) ++ /* stop charging, start discharging */ ++ tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | ++ OTGSC_CTRL_VBUS_DISCHARGE; ++ else ++ /* stop discharging */ ++ tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; ++ ++ fsl_writel(tmp, &usb_dr_regs->otgsc); ++} ++ ++/* A-device driver vbus, controlled through PP bit in PORTSC */ ++void fsl_otg_drv_vbus(int on) ++{ ++/* if (on) ++ usb_dr_regs->portsc = ++ cpu_to_le32((le32_to_cpu(usb_dr_regs->portsc) & ++ ~PORTSC_W1C_BITS) | PORTSC_PORT_POWER); ++ else ++ usb_dr_regs->portsc = ++ cpu_to_le32(le32_to_cpu(usb_dr_regs->portsc) & ++ ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER); ++*/ ++} ++ ++/* ++ * Pull-up D+, signalling connect by periperal. Also used in ++ * data-line pulsing in SRP ++ */ ++void fsl_otg_loc_conn(int on) ++{ ++ u32 tmp; ++ ++ tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; ++ ++ if (on) ++ tmp |= OTGSC_CTRL_DATA_PULSING; ++ else ++ tmp &= ~OTGSC_CTRL_DATA_PULSING; ++ ++ fsl_writel(tmp, &usb_dr_regs->otgsc); ++} ++ ++/* Generate SOF by host. This is controlled through suspend/resume the ++ * port. In host mode, controller will automatically send SOF. ++ * Suspend will block the data on the port. ++ */ ++void fsl_otg_loc_sof(int on) ++{ ++ u32 tmp; ++ ++ tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; ++ if (on) ++ tmp |= PORTSC_PORT_FORCE_RESUME; ++ else ++ tmp |= PORTSC_PORT_SUSPEND; ++ ++ fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); ++ ++} ++ ++/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ ++void fsl_otg_start_pulse(void) ++{ ++ u32 tmp; ++ ++ srp_wait_done = 0; ++#ifdef HA_DATA_PULSE ++ tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; ++ tmp |= OTGSC_HA_DATA_PULSE; ++ fsl_writel(tmp, &usb_dr_regs->otgsc); ++#else ++ fsl_otg_loc_conn(1); ++#endif ++ ++ fsl_otg_add_timer(b_data_pulse_tmr); ++} ++ ++void fsl_otg_pulse_vbus(void); ++ ++void b_data_pulse_end(unsigned long foo) ++{ ++#ifdef HA_DATA_PULSE ++#else ++ fsl_otg_loc_conn(0); ++#endif ++ ++ /* Do VBUS pulse after data pulse */ ++ fsl_otg_pulse_vbus(); ++} ++ ++void fsl_otg_pulse_vbus(void) ++{ ++ srp_wait_done = 0; ++ fsl_otg_chrg_vbus(1); ++ /* start the timer to end vbus charge */ ++ fsl_otg_add_timer(b_vbus_pulse_tmr); ++} ++ ++void b_vbus_pulse_end(unsigned long foo) ++{ ++ fsl_otg_chrg_vbus(0); ++ ++ /* As USB3300 using the same a_sess_vld and b_sess_vld voltage ++ * we need to discharge the bus for a while to distinguish ++ * residual voltage of vbus pulsing and A device pull up */ ++ fsl_otg_dischrg_vbus(1); ++ fsl_otg_add_timer(b_srp_wait_tmr); ++} ++ ++void b_srp_end(unsigned long foo) ++{ ++ fsl_otg_dischrg_vbus(0); ++ srp_wait_done = 1; ++ ++ if ((fsl_otg_dev->otg.state == OTG_STATE_B_SRP_INIT) && ++ fsl_otg_dev->fsm.b_sess_vld) ++ fsl_otg_dev->fsm.b_srp_done = 1; ++} ++ ++/* Workaround for a_host suspending too fast. When a_bus_req=0, ++ * a_host will start by SRP. It needs to set b_hnp_enable before ++ * actually suspending to start HNP ++ */ ++void a_wait_enum(unsigned long foo) ++{ ++ VDBG("a_wait_enum timeout\n"); ++ if (!fsl_otg_dev->otg.host->b_hnp_enable) ++ fsl_otg_add_timer(a_wait_enum_tmr); ++ else ++ otg_statemachine(&fsl_otg_dev->fsm); ++} ++ ++/* ------------------------------------------------------*/ ++ ++/* The timeout callback function to set time out bit */ ++void set_tmout(unsigned long indicator) ++{ ++ *(int *)indicator = 1; ++} ++ ++/* Initialize timers */ ++int fsl_otg_init_timers(struct otg_fsm *fsm) ++{ ++ /* FSM used timers */ ++ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, ++ (unsigned long)&fsm->a_wait_vrise_tmout); ++ if (a_wait_vrise_tmr == NULL) ++ return -ENOMEM; ++ ++ a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, ++ (unsigned long)&fsm->a_wait_bcon_tmout); ++ if (a_wait_bcon_tmr == NULL) ++ return -ENOMEM; ++ ++ a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, ++ (unsigned long)&fsm->a_aidl_bdis_tmout); ++ if (a_aidl_bdis_tmr == NULL) ++ return -ENOMEM; ++ ++ b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, ++ (unsigned long)&fsm->b_ase0_brst_tmout); ++ if (b_ase0_brst_tmr == NULL) ++ return -ENOMEM; ++ ++ b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, ++ (unsigned long)&fsm->b_se0_srp); ++ if (b_se0_srp_tmr == NULL) ++ return -ENOMEM; ++ ++ b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, ++ (unsigned long)&fsm->b_srp_done); ++ if (b_srp_fail_tmr == NULL) ++ return -ENOMEM; ++ ++ a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, ++ (unsigned long)&fsm); ++ if (a_wait_enum_tmr == NULL) ++ return -ENOMEM; ++ ++ /* device driver used timers */ ++ b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); ++ if (b_srp_wait_tmr == NULL) ++ return -ENOMEM; ++ ++ b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, ++ TB_DATA_PLS, 0); ++ if (b_data_pulse_tmr == NULL) ++ return -ENOMEM; ++ ++ b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, ++ TB_VBUS_PLS, 0); ++ if (b_vbus_pulse_tmr == NULL) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/* Uninitialize timers */ ++void fsl_otg_uninit_timers(void) ++{ ++ /* FSM used timers */ ++ if (a_wait_vrise_tmr != NULL) ++ kfree(a_wait_vrise_tmr); ++ if (a_wait_bcon_tmr != NULL) ++ kfree(a_wait_bcon_tmr); ++ if (a_aidl_bdis_tmr != NULL) ++ kfree(a_aidl_bdis_tmr); ++ if (b_ase0_brst_tmr != NULL) ++ kfree(b_ase0_brst_tmr); ++ if (b_se0_srp_tmr != NULL) ++ kfree(b_se0_srp_tmr); ++ if (b_srp_fail_tmr != NULL) ++ kfree(b_srp_fail_tmr); ++ if (a_wait_enum_tmr != NULL) ++ kfree(a_wait_enum_tmr); ++ ++ /* device driver used timers */ ++ if (b_srp_wait_tmr != NULL) ++ kfree(b_srp_wait_tmr); ++ if (b_data_pulse_tmr != NULL) ++ kfree(b_data_pulse_tmr); ++ if (b_vbus_pulse_tmr != NULL) ++ kfree(b_vbus_pulse_tmr); ++} ++ ++/* Add timer to timer list */ ++void fsl_otg_add_timer(void *gtimer) ++{ ++ struct fsl_otg_timer *timer = (struct fsl_otg_timer *)gtimer; ++ struct fsl_otg_timer *tmp_timer; ++ ++ /* 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; ++ list_add_tail(&timer->list, &active_timers); ++} ++ ++/* Remove timer from the timer list; clear timeout status */ ++void fsl_otg_del_timer(void *gtimer) ++{ ++ struct fsl_otg_timer *timer = (struct fsl_otg_timer *)gtimer; ++ struct fsl_otg_timer *tmp_timer, *del_tmp; ++ ++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) ++ if (tmp_timer == timer) ++ list_del(&timer->list); ++} ++ ++/* Reduce timer count by 1, and find timeout conditions. ++ * Called by fsl_otg 1ms timer interrupt ++ */ ++int fsl_otg_tick_timer(void) ++{ ++ struct fsl_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; ++ } ++ } ++ ++ return expired; ++} ++ ++/* Reset controller, not reset the bus */ ++void otg_reset_controller(void) ++{ ++ u32 command; ++ ++ command = fsl_readl(&usb_dr_regs->usbcmd); ++ command |= (1 << 1); ++ fsl_writel(command, &usb_dr_regs->usbcmd); ++ while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) ++ ; ++} ++ ++/* Call suspend/resume routines in host driver */ ++int fsl_otg_start_host(struct otg_fsm *fsm, int on) ++{ ++ struct otg_transceiver *xceiv = fsm->transceiver; ++ struct device *dev; ++ struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg); ++ u32 retval = 0; ++ ++ if (!xceiv->host) ++ return -ENODEV; ++ dev = xceiv->host->controller; ++ ++ /* Update a_vbus_vld state as a_vbus_vld int is disabled ++ * in device mode ++ */ ++ fsm->a_vbus_vld = ++ !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); ++ if (on) { ++ /* start fsl usb host controller */ ++ if (otg_dev->host_working) ++ goto end; ++ else { ++ otg_reset_controller(); ++ VDBG("host on......\n"); ++ if (dev->driver->resume) { ++ retval = dev->driver->resume(dev); ++ if (fsm->id) { ++ /* default-b */ ++ fsl_otg_drv_vbus(1); ++ /* Workaround: b_host can't driver ++ * vbus, but PP in PORTSC needs to ++ * be 1 for host to work. ++ * So we set drv_vbus bit in ++ * transceiver to 0 thru ULPI. */ ++ write_ulpi(0x0c, 0x20); ++ } ++ } ++ ++ otg_dev->host_working = 1; ++ } ++ } else { ++ /* stop fsl usb host controller */ ++ if (!otg_dev->host_working) ++ goto end; ++ else { ++ VDBG("host off......\n"); ++ if (dev && dev->driver) { ++ retval = dev->driver->suspend(dev, ++ otg_suspend_state); ++ if (fsm->id) ++ /* default-b */ ++ fsl_otg_drv_vbus(0); ++ } ++ otg_dev->host_working = 0; ++ } ++ } ++end: ++ return retval; ++} ++ ++/* Call suspend and resume function in udc driver ++ * to stop and start udc driver. ++ */ ++int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) ++{ ++ struct otg_transceiver *xceiv = fsm->transceiver; ++ struct device *dev; ++ ++ if (!xceiv->gadget || !xceiv->gadget->dev.parent) ++ return -ENODEV; ++ ++ VDBG("gadget %s\n", on ? "on" : "off"); ++ dev = xceiv->gadget->dev.parent; ++ ++ if (on) ++ dev->driver->resume(dev); ++ else ++ dev->driver->suspend(dev, otg_suspend_state); ++ ++ return 0; ++} ++ ++/* Called by initialization code of host driver. Register host controller ++ * to the OTG. Suspend host for OTG role detection. ++ */ ++static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) ++{ ++ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); ++ ++ if (!otg_p || otg_dev != fsl_otg_dev) ++ return -ENODEV; ++ ++ DBG("%s\n", __func__); ++ otg_p->host = host; ++ ++ otg_dev->fsm.a_bus_drop = 0; ++ otg_dev->fsm.a_bus_req = 1; ++ ++ if (host) { ++ VDBG("host off......\n"); ++ ++ otg_p->host->otg_port = fsl_otg_initdata.otg_port; ++ otg_p->host->is_b_host = otg_dev->fsm.id; ++ /* must leave time for khubd to finish its thing ++ * before yanking the host driver out from under it, ++ * so suspend the host after a short delay. ++ */ ++ otg_dev->host_working = 1; ++ schedule_delayed_work(&otg_dev->otg_event, 100); ++ return 0; ++ } else { /* host driver going away */ ++ ++ if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & ++ OTGSC_STS_USB_ID)) { ++ /* Mini-A cable connected */ ++ struct otg_fsm *fsm = &otg_dev->fsm; ++ ++ otg_p->state = OTG_STATE_UNDEFINED; ++ fsm->protocol = PROTO_UNDEF; ++ } ++ } ++ ++ otg_dev->host_working = 0; ++ ++ otg_statemachine(&otg_dev->fsm); ++ ++ return 0; ++} ++ ++/* Called by initialization code of udc. Register udc to OTG.*/ ++static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, ++ struct usb_gadget *gadget) ++{ ++ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); ++ ++ DBG("%s\n", __func__); ++ VDBG("otg_dev 0x%x\n", (int)otg_dev); ++ VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); ++ ++ if (!otg_p || otg_dev != fsl_otg_dev) ++ return -ENODEV; ++ ++ if (!gadget) { ++ if (!otg_dev->otg.default_a) ++ otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0); ++ usb_gadget_vbus_disconnect(otg_dev->otg.gadget); ++ otg_dev->otg.gadget = 0; ++ otg_dev->fsm.b_bus_req = 0; ++ otg_statemachine(&otg_dev->fsm); ++ return 0; ++ } ++ ++ otg_p->gadget = gadget; ++ otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id; ++ ++ otg_dev->fsm.b_bus_req = 1; ++ ++ /* start the gadget right away if the ID pin says Mini-B */ ++ DBG("ID pin=%d\n", otg_dev->fsm.id); ++ if (otg_dev->fsm.id == 1) { ++ fsl_otg_start_host(&otg_dev->fsm, 0); ++ otg_drv_vbus(&otg_dev->fsm, 0); ++ fsl_otg_start_gadget(&otg_dev->fsm, 1); ++ } ++ ++ return 0; ++} ++ ++/* Set OTG port power, only for B-device */ ++static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA) ++{ ++ if (!fsl_otg_dev) ++ return -ENODEV; ++ if (otg_p->state == OTG_STATE_B_PERIPHERAL) ++ printk(KERN_INFO "FSL OTG:Draw %d mA\n", mA); ++ ++ return 0; ++} ++ ++/* Delayed pin detect interrupt processing. ++ * ++ * When the Mini-A cable is disconnected from the board, ++ * the pin-detect interrupt happens before the disconnnect ++ * 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 ++ * by this routine. ++ */ ++static void fsl_otg_event(struct work_struct *work) ++{ ++ struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); ++ struct otg_fsm *fsm = &og->fsm; ++ ++ if (fsm->id) { /* switch to gadget */ ++ fsl_otg_start_host(fsm, 0); ++ otg_drv_vbus(fsm, 0); ++ fsl_otg_start_gadget(fsm, 1); ++ } ++} ++ ++/* B-device start SRP */ ++static int fsl_otg_start_srp(struct otg_transceiver *otg_p) ++{ ++ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); ++ ++ if (!otg_p || otg_dev != fsl_otg_dev ++ || otg_p->state != OTG_STATE_B_IDLE) ++ return -ENODEV; ++ ++ otg_dev->fsm.b_bus_req = 1; ++ otg_statemachine(&otg_dev->fsm); ++ ++ return 0; ++} ++ ++/* A_host suspend will call this function to start hnp */ ++static int fsl_otg_start_hnp(struct otg_transceiver *otg_p) ++{ ++ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); ++ ++ if (!otg_p || otg_dev != fsl_otg_dev) ++ return -ENODEV; ++ ++ /* printk("start_hnp.............\n"); */ ++ /* clear a_bus_req to enter a_suspend state */ ++ otg_dev->fsm.a_bus_req = 0; ++ otg_statemachine(&otg_dev->fsm); ++ ++ return 0; ++} ++ ++/* Interrupt handler. OTG/host/peripheral share the same int line. ++ * OTG driver clears OTGSC interrupts and leaves USB interrupts ++ * intact. It needs to have knowledge of some USB interrupts ++ * such as port change. ++ */ ++irqreturn_t fsl_otg_isr(int irq, void *dev_id) ++{ ++ struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; ++ struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg; ++ u32 otg_int_src, otg_sc; ++ ++ otg_sc = fsl_readl(&usb_dr_regs->otgsc); ++ otg_int_src = otg_sc & OTGSC_INTSTS_MASK; ++ ++ /* Only clear otg interrupts */ ++ fsl_writel(otg_sc, &usb_dr_regs->otgsc); ++ ++ /*FIXME: ID change not generate when init to 0 */ ++ fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; ++ otg->default_a = (fsm->id == 0); ++ ++ /* process OTG interrupts */ ++ if (otg_int_src) { ++ if (otg_int_src & OTGSC_INTSTS_USB_ID) { ++ fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; ++ otg->default_a = (fsm->id == 0); ++ /* clear conn information */ ++ if (fsm->id) ++ fsm->b_conn = 0; ++ else ++ fsm->a_conn = 0; ++ ++ if (otg->host) ++ otg->host->is_b_host = fsm->id; ++ if (otg->gadget) ++ otg->gadget->is_a_peripheral = !fsm->id; ++ VDBG("ID int (ID is %d)\n", fsm->id); ++ ++ if (fsm->id) { /* switch to gadget */ ++ schedule_delayed_work(&((struct fsl_otg *) ++ dev_id)->otg_event, 100); ++ } else { /* switch to host */ ++ cancel_delayed_work(& ++ ((struct fsl_otg *)dev_id)-> ++ otg_event); ++ fsl_otg_start_gadget(fsm, 0); ++ otg_drv_vbus(fsm, 1); ++ fsl_otg_start_host(fsm, 1); ++ } ++ return IRQ_HANDLED; ++ } ++ } ++ return IRQ_NONE; ++} ++ ++static struct otg_fsm_ops fsl_otg_ops = { ++ .chrg_vbus = fsl_otg_chrg_vbus, ++ .drv_vbus = fsl_otg_drv_vbus, ++ .loc_conn = fsl_otg_loc_conn, ++ .loc_sof = fsl_otg_loc_sof, ++ .start_pulse = fsl_otg_start_pulse, ++ ++ .add_timer = fsl_otg_add_timer, ++ .del_timer = fsl_otg_del_timer, ++ ++ .start_host = fsl_otg_start_host, ++ .start_gadget = fsl_otg_start_gadget, ++}; ++ ++/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ ++static int fsl_otg_conf(struct platform_device *pdev) ++{ ++ int status; ++ struct fsl_otg *fsl_otg_tc; ++ struct fsl_usb2_platform_data *pdata; ++ ++ pdata = pdev->dev.platform_data; ++ ++ if (fsl_otg_dev) ++ return 0; ++ ++ /* allocate space to fsl otg device */ ++ fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); ++ if (!fsl_otg_tc) ++ return -ENODEV; ++ ++ INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); ++ ++ INIT_LIST_HEAD(&active_timers); ++ status = fsl_otg_init_timers(&fsl_otg_tc->fsm); ++ if (status) { ++ printk(KERN_INFO "Couldn't init OTG timers\n"); ++ fsl_otg_uninit_timers(); ++ kfree(fsl_otg_tc); ++ return status; ++ } ++ spin_lock_init(&fsl_otg_tc->fsm.lock); ++ ++ /* Set OTG state machine operations */ ++ fsl_otg_tc->fsm.ops = &fsl_otg_ops; ++ ++ /* initialize the otg structure */ ++ fsl_otg_tc->otg.label = DRIVER_DESC; ++ fsl_otg_tc->otg.set_host = fsl_otg_set_host; ++ fsl_otg_tc->otg.set_peripheral = fsl_otg_set_peripheral; ++ fsl_otg_tc->otg.set_power = fsl_otg_set_power; ++ fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp; ++ fsl_otg_tc->otg.start_srp = fsl_otg_start_srp; ++ ++ fsl_otg_dev = fsl_otg_tc; ++ ++ /* Store the otg transceiver */ ++ status = otg_set_transceiver(&fsl_otg_tc->otg); ++ if (status) { ++ printk(KERN_WARNING ": unable to register OTG transceiver.\n"); ++ return status; ++ } ++ ++ return 0; ++} ++ ++/* OTG Initialization*/ ++int usb_otg_start(struct platform_device *pdev) ++{ ++ struct fsl_otg __iomem *p_otg; ++ struct otg_transceiver *otg_trans = otg_get_transceiver(); ++ struct otg_fsm *fsm; ++ volatile unsigned long *p; ++ int status; ++ struct resource *res; ++ u32 temp; ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ p_otg = container_of(otg_trans, struct fsl_otg, otg); ++ fsm = &p_otg->fsm; ++ ++ /* Initialize the state machine structure with default values */ ++ SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); ++ fsm->transceiver = &p_otg->otg; ++ ++ /* We don't require predefined MEM/IRQ resource index */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENXIO; ++ ++ /* We don't request_mem_region here to enable resource sharing ++ * with host/device */ ++ ++ usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); ++ p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; ++ pdata->regs = (void *)usb_dr_regs; ++ ++ /* request irq */ ++ p_otg->irq = platform_get_irq(pdev, 0); ++ status = request_irq(p_otg->irq, fsl_otg_isr, ++ IRQF_SHARED, driver_name, p_otg); ++ if (status) { ++ dev_dbg(p_otg->otg.dev, "can't get IRQ %d, error %d\n", ++ p_otg->irq, status); ++ iounmap(p_otg->dr_mem_map); ++ kfree(p_otg); ++ return status; ++ } ++ ++ if (pdata->init && pdata->init(pdev) != 0) ++ return -EINVAL; ++ ++ ++ /* Export DR controller resources */ ++ otg_set_resources(pdev->resource); ++ ++ /* stop the controller */ ++ temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); ++ temp &= ~USB_CMD_RUN_STOP; ++ fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); ++ ++ /* reset the controller */ ++ temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); ++ temp |= USB_CMD_CTRL_RESET; ++ fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); ++ ++ /* wait reset completed */ ++ udelay(2); ++ while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) ++ ; ++ ++ /* configure the VBUSHS as IDLE(both host and device) */ ++ temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); ++ fsl_writel(temp, &p_otg->dr_mem_map->usbmode); ++ ++ /* configure PHY interface */ ++ temp = fsl_readl(&p_otg->dr_mem_map->portsc); ++ temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); ++ switch (pdata->phy_mode) { ++ case FSL_USB2_PHY_ULPI: ++ temp |= PORTSC_PTS_ULPI; ++ break; ++ case FSL_USB2_PHY_UTMI_WIDE: ++ temp |= PORTSC_PTW_16BIT; ++ /* fall through */ ++ case FSL_USB2_PHY_UTMI: ++ temp |= PORTSC_PTS_UTMI; ++ /* fall through */ ++ default: ++ break; ++ } ++ fsl_writel(temp, &p_otg->dr_mem_map->portsc); ++ ++ if (pdata->have_sysif_regs) { ++ /* configure control enable IO output, big endian register */ ++ p = (volatile unsigned long *)(&p_otg->dr_mem_map->control); ++ temp = *p; ++ temp |= USB_CTRL_IOENB; ++ *p = temp; ++ } ++ ++ /* disable all interrupt and clear all OTGSC status */ ++ temp = fsl_readl(&p_otg->dr_mem_map->otgsc); ++ temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; ++ temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; ++ fsl_writel(temp, &p_otg->dr_mem_map->otgsc); ++ ++ ++ /* ++ * The identification (id) input is FALSE when a Mini-A plug is inserted ++ * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. ++ * Also: record initial state of ID pin ++ */ ++ if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { ++ p_otg->otg.state = OTG_STATE_UNDEFINED; ++ p_otg->fsm.id = 1; ++ } else { ++ p_otg->otg.state = OTG_STATE_A_IDLE; ++ p_otg->fsm.id = 0; ++ } ++ ++ DBG("initial ID pin=%d\n", p_otg->fsm.id); ++ ++ /* enable OTG ID pin interrupt */ ++ temp = fsl_readl(&p_otg->dr_mem_map->otgsc); ++ temp |= OTGSC_INTR_USB_ID_EN; ++ temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); ++ fsl_writel(temp, &p_otg->dr_mem_map->otgsc); ++ ++ return 0; ++} ++ ++/*------------------------------------------------------------------------- ++ PROC File System Support ++-------------------------------------------------------------------------*/ ++#ifdef CONFIG_USB_OTG_DEBUG_FILES ++ ++#include <linux/seq_file.h> ++ ++static const char proc_filename[] = "driver/fsl_usb2_otg"; ++ ++static int otg_proc_read(char *page, char **start, off_t off, int count, ++ int *eof, void *_dev) ++{ ++ struct otg_fsm *fsm = &fsl_otg_dev->fsm; ++ char *buf = page; ++ char *next = buf; ++ unsigned size = count; ++ unsigned long flags; ++ int t; ++ u32 tmp_reg; ++ ++ if (off != 0) ++ return 0; ++ ++ spin_lock_irqsave(&fsm->lock, flags); ++ ++ /* ------basic driver infomation ---- */ ++ t = scnprintf(next, size, ++ DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", ++ DRIVER_VERSION); ++ size -= t; ++ next += t; ++ ++ /* ------ Registers ----- */ ++ tmp_reg = fsl_readl(&usb_dr_regs->otgsc); ++ t = scnprintf(next, size, "OTGSC reg: %08x\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ tmp_reg = fsl_readl(&usb_dr_regs->portsc); ++ t = scnprintf(next, size, "PORTSC reg: %08x\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ tmp_reg = fsl_readl(&usb_dr_regs->usbmode); ++ t = scnprintf(next, size, "USBMODE reg: %08x\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ tmp_reg = fsl_readl(&usb_dr_regs->usbcmd); ++ t = scnprintf(next, size, "USBCMD reg: %08x\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ tmp_reg = fsl_readl(&usb_dr_regs->usbsts); ++ t = scnprintf(next, size, "USBSTS reg: %08x\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ tmp_reg = fsl_readl(&usb_dr_regs->usbintr); ++ t = scnprintf(next, size, "USBINTR reg: %08x\n", tmp_reg); ++ size -= t; ++ next += t; ++ ++ /* ------ State ----- */ ++ t = scnprintf(next, size, ++ "OTG state: %s\n\n", ++ state_string(fsl_otg_dev->otg.state)); ++ size -= t; ++ next += t; ++ ++ /* ------ State Machine Variables ----- */ ++ t = scnprintf(next, size, "a_bus_req: %d\n", fsm->a_bus_req); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_bus_req: %d\n", fsm->b_bus_req); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "a_bus_resume: %d\n", fsm->a_bus_resume); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "a_bus_suspend: %d\n", fsm->a_bus_suspend); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "a_conn: %d\n", fsm->a_conn); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "a_sess_vld: %d\n", fsm->a_sess_vld); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "a_srp_det: %d\n", fsm->a_srp_det); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "a_vbus_vld: %d\n", fsm->a_vbus_vld); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_bus_resume: %d\n", fsm->b_bus_resume); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_bus_suspend: %d\n", fsm->b_bus_suspend); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_conn: %d\n", fsm->b_conn); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_se0_srp: %d\n", fsm->b_se0_srp); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_sess_end: %d\n", fsm->b_sess_end); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "b_sess_vld: %d\n", fsm->b_sess_vld); ++ size -= t; ++ next += t; ++ ++ t = scnprintf(next, size, "id: %d\n", fsm->id); ++ size -= t; ++ next += t; ++ ++ spin_unlock_irqrestore(&fsm->lock, flags); ++ ++ *eof = 1; ++ return count - size; ++} ++ ++#define create_proc_file() create_proc_read_entry(proc_filename, \ ++ 0, NULL, otg_proc_read, NULL) ++ ++#define remove_proc_file() remove_proc_entry(proc_filename, NULL) ++ ++#else /* !CONFIG_USB_OTG_DEBUG_FILES */ ++ ++#define create_proc_file() do {} while (0) ++#define remove_proc_file() do {} while (0) ++ ++#endif /*CONFIG_USB_OTG_DEBUG_FILES */ ++ ++/*----------------------------------------------------------*/ ++/* Char driver interface to control some OTG input */ ++ ++/* This function handle some ioctl command,such as get otg ++ * status and set host suspend ++ */ ++static long fsl_otg_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ u32 retval = 0; ++ ++ switch (cmd) { ++ case GET_OTG_STATUS: ++ retval = fsl_otg_dev->host_working; ++ break; ++ ++ case SET_A_SUSPEND_REQ: ++ fsl_otg_dev->fsm.a_suspend_req = arg; ++ break; ++ ++ case SET_A_BUS_DROP: ++ fsl_otg_dev->fsm.a_bus_drop = arg; ++ break; ++ ++ case SET_A_BUS_REQ: ++ fsl_otg_dev->fsm.a_bus_req = arg; ++ break; ++ ++ case SET_B_BUS_REQ: ++ fsl_otg_dev->fsm.b_bus_req = arg; ++ break; ++ ++ default: ++ break; ++ } ++ ++ otg_statemachine(&fsl_otg_dev->fsm); ++ ++ return retval; ++} ++ ++static int fsl_otg_open(struct inode *inode, struct file *file) ++{ ++ ++ return 0; ++} ++ ++static int fsl_otg_release(struct inode *inode, struct file *file) ++{ ++ ++ return 0; ++} ++ ++static struct file_operations otg_fops = { ++ .owner = THIS_MODULE, ++ .llseek = NULL, ++ .read = NULL, ++ .write = NULL, ++ .unlocked_ioctl = fsl_otg_ioctl, ++ .open = fsl_otg_open, ++ .release = fsl_otg_release, ++}; ++ ++static int __init fsl_otg_probe(struct platform_device *pdev) ++{ ++ int status; ++ struct fsl_usb2_platform_data *pdata; ++ ++ DBG("pdev=0x%p\n", pdev); ++ ++ if (!pdev) ++ return -ENODEV; ++ ++ if (!pdev->dev.platform_data) ++ return -ENOMEM; ++ ++ pdata = pdev->dev.platform_data; ++ fsl_set_usb_accessors(pdata); ++ ++ /* configure the OTG */ ++ status = fsl_otg_conf(pdev); ++ if (status) { ++ printk(KERN_INFO "Couldn't init OTG module\n"); ++ return -status; ++ } ++ ++ /* start OTG */ ++ status = usb_otg_start(pdev); ++ ++ if (register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops)) { ++ printk(KERN_WARNING FSL_OTG_NAME ++ ": unable to register FSL OTG device\n"); ++ return -EIO; ++ } ++ ++ create_proc_file(); ++ return status; ++} ++ ++static int __exit fsl_otg_remove(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ otg_set_transceiver(NULL); ++ free_irq(fsl_otg_dev->irq, fsl_otg_dev); ++ ++ iounmap((void *)usb_dr_regs); ++ ++ kfree(fsl_otg_dev); ++ ++ remove_proc_file(); ++ ++ unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); ++ ++ if (pdata->exit) ++ pdata->exit(pdev); ++ ++ return 0; ++} ++ ++struct platform_driver fsl_otg_driver = { ++ .probe = fsl_otg_probe, ++ .remove = __exit_p(fsl_otg_remove), ++ .driver = { ++ .name = driver_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init fsl_usb_otg_init(void) ++{ ++ printk(KERN_INFO DRIVER_DESC " loaded, %s\n", DRIVER_VERSION); ++ return platform_driver_register(&fsl_otg_driver); ++} ++ ++static void __exit fsl_usb_otg_exit(void) ++{ ++ platform_driver_unregister(&fsl_otg_driver); ++ printk(KERN_INFO DRIVER_DESC " unloaded\n"); ++} ++ ++module_init(fsl_usb_otg_init); ++module_exit(fsl_usb_otg_exit); ++ ++MODULE_DESCRIPTION(DRIVER_INFO); ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/usb/otg/fsl_otg.h +@@ -0,0 +1,423 @@ ++/* Copyright 2005-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "otg_fsm.h" ++#include <linux/usb/otg.h> ++#include <linux/ioctl.h> ++ ++#ifdef CONFIG_PPC32 ++#include <asm/fsl_usb_io.h> ++#elif CONFIG_COLDFIRE ++#include <asm/mcfsim.h> ++#include <asm/fsl_usb_io.h> ++#endif ++ ++#define hc32_to_cpu(x) (x) ++#define cpu_to_hc32(x) (x) ++ ++ /* USB Command Register Bit Masks */ ++#define USB_CMD_RUN_STOP (0x1<<0) ++#define USB_CMD_CTRL_RESET (0x1<<1) ++#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) ++#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) ++#define USB_CMD_INT_AA_DOORBELL (0x1<<6) ++#define USB_CMD_ASP (0x3<<8) ++#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) ++#define USB_CMD_SUTW (0x1<<13) ++#define USB_CMD_ATDTW (0x1<<14) ++#define USB_CMD_ITC (0xFF<<16) ++ ++/* bit 15,3,2 are frame list size */ ++#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) ++#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) ++#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) ++#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) ++#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) ++#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) ++#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) ++#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) ++ ++/* bit 9-8 are async schedule park mode count */ ++#define USB_CMD_ASP_00 (0x0<<8) ++#define USB_CMD_ASP_01 (0x1<<8) ++#define USB_CMD_ASP_10 (0x2<<8) ++#define USB_CMD_ASP_11 (0x3<<8) ++#define USB_CMD_ASP_BIT_POS (8) ++ ++/* bit 23-16 are interrupt threshold control */ ++#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) ++#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) ++#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) ++#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) ++#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) ++#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) ++#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) ++#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) ++#define USB_CMD_ITC_BIT_POS (16) ++ ++/* USB Status Register Bit Masks */ ++#define USB_STS_INT (0x1<<0) ++#define USB_STS_ERR (0x1<<1) ++#define USB_STS_PORT_CHANGE (0x1<<2) ++#define USB_STS_FRM_LST_ROLL (0x1<<3) ++#define USB_STS_SYS_ERR (0x1<<4) ++#define USB_STS_IAA (0x1<<5) ++#define USB_STS_RESET_RECEIVED (0x1<<6) ++#define USB_STS_SOF (0x1<<7) ++#define USB_STS_DCSUSPEND (0x1<<8) ++#define USB_STS_HC_HALTED (0x1<<12) ++#define USB_STS_RCL (0x1<<13) ++#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) ++#define USB_STS_ASYNC_SCHEDULE (0x1<<15) ++ ++/* USB Interrupt Enable Register Bit Masks */ ++#define USB_INTR_INT_EN (0x1<<0) ++#define USB_INTR_ERR_INT_EN (0x1<<1) ++#define USB_INTR_PC_DETECT_EN (0x1<<2) ++#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) ++#define USB_INTR_SYS_ERR_EN (0x1<<4) ++#define USB_INTR_ASYN_ADV_EN (0x1<<5) ++#define USB_INTR_RESET_EN (0x1<<6) ++#define USB_INTR_SOF_EN (0x1<<7) ++#define USB_INTR_DEVICE_SUSPEND (0x1<<8) ++ ++/* Device Address bit masks */ ++#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) ++#define USB_DEVICE_ADDRESS_BIT_POS (25) ++/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ ++#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) ++#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) ++#define PORTSC_PORT_ENABLE (0x1<<2) ++#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) ++#define PORTSC_OVER_CURRENT_ACT (0x1<<4) ++#define PORTSC_OVER_CUURENT_CHG (0x1<<5) ++#define PORTSC_PORT_FORCE_RESUME (0x1<<6) ++#define PORTSC_PORT_SUSPEND (0x1<<7) ++#define PORTSC_PORT_RESET (0x1<<8) ++#define PORTSC_LINE_STATUS_BITS (0x3<<10) ++#define PORTSC_PORT_POWER (0x1<<12) ++#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) ++#define PORTSC_PORT_TEST_CTRL (0xF<<16) ++#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) ++#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) ++#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) ++#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) ++#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) ++#define PORTSC_PORT_SPEED_MASK (0x3<<26) ++#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) ++#define PORTSC_PHY_TYPE_SEL (0x3<<30) ++/* bit 11-10 are line status */ ++#define PORTSC_LINE_STATUS_SE0 (0x0<<10) ++#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) ++#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) ++#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) ++#define PORTSC_LINE_STATUS_BIT_POS (10) ++ ++/* bit 15-14 are port indicator control */ ++#define PORTSC_PIC_OFF (0x0<<14) ++#define PORTSC_PIC_AMBER (0x1<<14) ++#define PORTSC_PIC_GREEN (0x2<<14) ++#define PORTSC_PIC_UNDEF (0x3<<14) ++#define PORTSC_PIC_BIT_POS (14) ++ ++/* bit 19-16 are port test control */ ++#define PORTSC_PTC_DISABLE (0x0<<16) ++#define PORTSC_PTC_JSTATE (0x1<<16) ++#define PORTSC_PTC_KSTATE (0x2<<16) ++#define PORTSC_PTC_SEQNAK (0x3<<16) ++#define PORTSC_PTC_PACKET (0x4<<16) ++#define PORTSC_PTC_FORCE_EN (0x5<<16) ++#define PORTSC_PTC_BIT_POS (16) ++ ++/* bit 27-26 are port speed */ ++#define PORTSC_PORT_SPEED_FULL (0x0<<26) ++#define PORTSC_PORT_SPEED_LOW (0x1<<26) ++#define PORTSC_PORT_SPEED_HIGH (0x2<<26) ++#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) ++#define PORTSC_SPEED_BIT_POS (26) ++ ++/* bit 28 is parallel transceiver width for UTMI interface */ ++#define PORTSC_PTW (0x1<<28) ++#define PORTSC_PTW_8BIT (0x0<<28) ++#define PORTSC_PTW_16BIT (0x1<<28) ++ ++/* bit 31-30 are port transceiver select */ ++#define PORTSC_PTS_UTMI (0x0<<30) ++#define PORTSC_PTS_ULPI (0x2<<30) ++#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) ++#define PORTSC_PTS_BIT_POS (30) ++ ++#define PORTSC_W1C_BITS \ ++ (PORTSC_CONNECT_STATUS_CHANGE | \ ++ PORTSC_PORT_EN_DIS_CHANGE | \ ++ PORTSC_OVER_CUURENT_CHG) ++ ++/* OTG Status Control Register Bit Masks */ ++#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) ++#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) ++#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) ++#define OTGSC_CTRL_DATA_PULSING (0x1<<4) ++#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) ++#define OTGSC_HA_DATA_PULSE (0x1<<6) ++#define OTGSC_HA_BA (0x1<<7) ++#define OTGSC_STS_USB_ID (0x1<<8) ++#define OTGSC_STS_A_VBUS_VALID (0x1<<9) ++#define OTGSC_STS_A_SESSION_VALID (0x1<<10) ++#define OTGSC_STS_B_SESSION_VALID (0x1<<11) ++#define OTGSC_STS_B_SESSION_END (0x1<<12) ++#define OTGSC_STS_1MS_TOGGLE (0x1<<13) ++#define OTGSC_STS_DATA_PULSING (0x1<<14) ++#define OTGSC_INTSTS_USB_ID (0x1<<16) ++#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) ++#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) ++#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) ++#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) ++#define OTGSC_INTSTS_1MS (0x1<<21) ++#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) ++#define OTGSC_INTR_USB_ID_EN (0x1<<24) ++#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) ++#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) ++#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) ++#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) ++#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) ++#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) ++#define OTGSC_INTSTS_MASK (0x00ff0000) ++ ++/* USB MODE Register Bit Masks */ ++#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) ++#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) ++#define USB_MODE_CTRL_MODE_HOST (0x3<<0) ++#define USB_MODE_CTRL_MODE_RSV (0x1<<0) ++#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) ++#define USB_MODE_STREAM_DISABLE (0x1<<4) ++#define USB_MODE_ES (0x1<<2) /* (big) Endian Select */ ++ ++#define MPC8349_OTG_IRQ (38) ++#define CFG_IMMR_BASE (0xfe000000) ++#define MPC83xx_USB_DR_BASE (CFG_IMMR_BASE + 0x23000) ++ ++/* control Register Bit Masks */ ++#define USB_CTRL_IOENB (0x1<<2) ++#define USB_CTRL_ULPI_INT0EN (0x1<<0) ++ ++/* BCSR5 */ ++#define BCSR5_INT_USB (0x02) ++ ++/* USB module clk cfg */ ++#define SCCR_OFFS (0xA08) ++#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ ++#define SCCR_USB_MPHCM_11 (0x00c00000) ++#define SCCR_USB_MPHCM_01 (0x00400000) ++#define SCCR_USB_MPHCM_10 (0x00800000) ++#define SCCR_USB_DRCM_11 (0x00300000) ++#define SCCR_USB_DRCM_01 (0x00100000) ++#define SCCR_USB_DRCM_10 (0x00200000) ++ ++#define SICRL_OFFS (0x114) ++#define SICRL_USB0 (0x40000000) ++#define SICRL_USB1 (0x20000000) ++ ++#define SICRH_OFFS (0x118) ++#define SICRH_USB_UTMI (0x00020000) ++ ++/* OTG interrupt enable bit masks */ ++#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ ++ (OTGSC_INTR_USB_ID_EN | \ ++ OTGSC_INTR_1MS_TIMER_EN | \ ++ OTGSC_INTR_A_VBUS_VALID_EN | \ ++ OTGSC_INTR_A_SESSION_VALID_EN | \ ++ OTGSC_INTR_B_SESSION_VALID_EN | \ ++ OTGSC_INTR_B_SESSION_END_EN | \ ++ OTGSC_INTR_DATA_PULSING_EN) ++ ++/* OTG interrupt status bit masks */ ++#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ ++ (OTGSC_INTSTS_USB_ID | \ ++ OTGSC_INTR_1MS_TIMER_EN | \ ++ OTGSC_INTSTS_A_VBUS_VALID | \ ++ OTGSC_INTSTS_A_SESSION_VALID | \ ++ OTGSC_INTSTS_B_SESSION_VALID | \ ++ OTGSC_INTSTS_B_SESSION_END | \ ++ OTGSC_INTSTS_DATA_PULSING) ++ ++/* ++ * A-DEVICE timing constants ++ */ ++ ++/* Wait for VBUS Rise */ ++#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ ++ ++/* Wait for B-Connect */ ++#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 ++ * This is only used to get out of ++ * OTG_STATE_A_WAIT_BCON state if there was ++ * no connection for these many milliseconds ++ */ ++ ++/* A-Idle to B-Disconnect */ ++/* It is necessary for this timer to be more than 750 ms because of a bug in OPT ++ * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated ++ * in the test description ++ */ ++#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ ++ ++/* B-Idle to A-Disconnect */ ++#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ ++ ++/* B-device timing constants */ ++ ++ ++/* Data-Line Pulse Time*/ ++#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ ++#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ ++#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ ++ ++/* SRP Initiate Time */ ++#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ ++ ++/* SRP Fail Time */ ++#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ ++ ++/* SRP result wait time */ ++#define TB_SRP_WAIT (60) ++ ++/* VBus time */ ++#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ ++ ++/* Discharge time */ ++/* This time should be less than 10ms. It varies from system to system. */ ++#define TB_VBUS_DSCHRG (8) ++ ++/* A-SE0 to B-Reset */ ++#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ ++ ++/* A bus suspend timer before we can switch to b_wait_aconn */ ++#define TB_A_SUSPEND (7) ++#define TB_BUS_RESUME (12) ++ ++/* SE0 Time Before SRP */ ++#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ ++ ++ ++#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) ++ ++struct usb_dr_mmap { ++ /* Capability register */ ++ u8 res1[256]; ++ u16 caplength; /* Capability Register Length */ ++ u16 hciversion; /* Host Controller Interface Version */ ++ u32 hcsparams; /* Host Controller Structual Parameters */ ++ u32 hccparams; /* Host Controller Capability Parameters */ ++ u8 res2[20]; ++ u32 dciversion; /* Device Controller Interface Version */ ++ u32 dccparams; /* Device Controller Capability Parameters */ ++ u8 res3[24]; ++ /* Operation register */ ++ u32 usbcmd; /* USB Command Register */ ++ u32 usbsts; /* USB Status Register */ ++ u32 usbintr; /* USB Interrupt Enable Register */ ++ u32 frindex; /* Frame Index Register */ ++ u8 res4[4]; ++ u32 deviceaddr; /* Device Address */ ++ u32 endpointlistaddr; /* Endpoint List Address Register */ ++ u8 res5[4]; ++ u32 burstsize; /* Master Interface Data Burst Size Register */ ++ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ ++ u8 res6[8]; ++ u32 ulpiview; /* ULPI register access */ ++ u8 res7[12]; ++ u32 configflag; /* Configure Flag Register */ ++ u32 portsc; /* Port 1 Status and Control Register */ ++ u8 res8[28]; ++ u32 otgsc; /* On-The-Go Status and Control */ ++ u32 usbmode; /* USB Mode Register */ ++ u32 endptsetupstat; /* Endpoint Setup Status Register */ ++ u32 endpointprime; /* Endpoint Initialization Register */ ++ u32 endptflush; /* Endpoint Flush Register */ ++ u32 endptstatus; /* Endpoint Status Register */ ++ u32 endptcomplete; /* Endpoint Complete Register */ ++ u32 endptctrl[6]; /* Endpoint Control Registers */ ++ u8 res9[552]; ++ u32 snoop1; ++ u32 snoop2; ++ u32 age_cnt_thresh; /* Age Count Threshold Register */ ++ u32 pri_ctrl; /* Priority Control Register */ ++ u32 si_ctrl; /* System Interface Control Register */ ++ u8 res10[236]; ++ u32 control; /* General Purpose Control Register */ ++}; ++ ++ ++struct fsl_otg_timer { ++ unsigned long expires; /* Number of count increase to timeout */ ++ unsigned long count; /* Tick counter */ ++ void (*function)(unsigned long); /* Timeout function */ ++ unsigned long data; /* Data passed to function */ ++ struct list_head list; ++}; ++ ++static inline struct fsl_otg_timer *otg_timer_initializer ++(void (*function)(unsigned long), unsigned long expires, unsigned long data) ++{ ++ struct fsl_otg_timer *timer; ++ timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); ++ if (timer == NULL) ++ return NULL; ++ timer->function = function; ++ timer->expires = expires; ++ timer->data = data; ++ return timer; ++} ++ ++struct fsl_otg { ++ struct otg_transceiver otg; ++ struct otg_fsm fsm; ++ struct usb_dr_mmap __iomem *dr_mem_map; ++ struct delayed_work otg_event; ++ ++ /* used for usb host */ ++ struct work_struct work_wq; ++ u8 host_working; ++ ++ int irq; ++}; ++ ++struct fsl_otg_config { ++ u8 otg_port; ++}; ++ ++/*For SRP and HNP handle*/ ++#define FSL_OTG_MAJOR 66 ++#define FSL_OTG_NAME "fsl-usb2-otg" ++/*Command to OTG driver(ioctl)*/ ++#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR ++/*if otg work as host,it should return 1,otherwise it return 0*/ ++#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) ++#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) ++#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) ++#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) ++#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) ++#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) ++#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) ++#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) ++#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) ++ ++extern const char *state_string(enum usb_otg_state state); ++extern int otg_set_resources(struct resource *resources); ++/* prototype declaration */ ++extern void fsl_otg_add_timer(void *timer); ++extern void fsl_otg_del_timer(void *timer); +--- /dev/null ++++ b/drivers/usb/otg/otg_fsm.c +@@ -0,0 +1,371 @@ ++/* OTG Finite State Machine from OTG spec ++ * ++ * Copyright 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Author: Li Yang <LeoLi@freescale.com> ++ * Jerry Huang <Chang-Ming.Huang@freescale.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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/usb/otg.h> ++#include <linux/spinlock.h> ++#include <linux/delay.h> ++#include <linux/usb.h> ++#include <linux/usb/gadget.h> ++ ++#include <linux/types.h> ++#include "otg_fsm.h" ++ ++ ++/* Defined by device specific driver, for different timer implementation */ ++extern void *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, ++ *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, *a_wait_enum_tmr; ++ ++const char *state_string(enum usb_otg_state state) ++{ ++ switch (state) { ++ case OTG_STATE_A_IDLE: return "a_idle"; ++ case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; ++ case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; ++ case OTG_STATE_A_HOST: return "a_host"; ++ case OTG_STATE_A_SUSPEND: return "a_suspend"; ++ case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; ++ case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; ++ case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; ++ case OTG_STATE_B_IDLE: return "b_idle"; ++ case OTG_STATE_B_SRP_INIT: return "b_srp_init"; ++ case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; ++ case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; ++ case OTG_STATE_B_HOST: return "b_host"; ++ default: return "UNDEFINED"; ++ } ++} ++ ++/* Change USB protocol when there is a protocol change */ ++static int otg_set_protocol(struct otg_fsm *fsm, int protocol) ++{ ++ int ret = 0; ++ ++ if (fsm->protocol != protocol) { ++ VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", ++ fsm->protocol, protocol); ++ /* stop old protocol */ ++ if (fsm->protocol == PROTO_HOST) ++ ret = fsm->ops->start_host(fsm, 0); ++ else if (fsm->protocol == PROTO_GADGET) ++ ret = fsm->ops->start_gadget(fsm, 0); ++ if (ret) ++ return ret; ++ ++ /* start new protocol */ ++ if (protocol == PROTO_HOST) ++ ret = fsm->ops->start_host(fsm, 1); ++ else if (protocol == PROTO_GADGET) ++ ret = fsm->ops->start_gadget(fsm, 1); ++ if (ret) ++ return ret; ++ ++ fsm->protocol = protocol; ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static int state_changed; ++ ++/* Called when leaving a state. Do state clean up jobs here */ ++void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) ++{ ++ switch (old_state) { ++ case OTG_STATE_B_IDLE: ++ otg_del_timer(fsm, b_se0_srp_tmr); ++ fsm->b_se0_srp = 0; ++ break; ++ case OTG_STATE_B_SRP_INIT: ++ fsm->b_srp_done = 0; ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ break; ++ case OTG_STATE_B_WAIT_ACON: ++ otg_del_timer(fsm, b_ase0_brst_tmr); ++ fsm->b_ase0_brst_tmout = 0; ++ break; ++ case OTG_STATE_B_HOST: ++ break; ++ case OTG_STATE_A_IDLE: ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ otg_del_timer(fsm, a_wait_vrise_tmr); ++ fsm->a_wait_vrise_tmout = 0; ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ otg_del_timer(fsm, a_wait_bcon_tmr); ++ fsm->a_wait_bcon_tmout = 0; ++ break; ++ case OTG_STATE_A_HOST: ++ otg_del_timer(fsm, a_wait_enum_tmr); ++ break; ++ case OTG_STATE_A_SUSPEND: ++ otg_del_timer(fsm, a_aidl_bdis_tmr); ++ fsm->a_aidl_bdis_tmout = 0; ++ fsm->a_suspend_req = 0; ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ break; ++ case OTG_STATE_A_WAIT_VFALL: ++ otg_del_timer(fsm, a_wait_vrise_tmr); ++ break; ++ case OTG_STATE_A_VBUS_ERR: ++ break; ++ default: ++ break; ++ } ++} ++ ++/* Called when entering a state */ ++int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) ++{ ++ state_changed = 1; ++ if (fsm->transceiver->state == new_state) ++ return 0; ++ VDBG("Set state: %s\n", state_string(new_state)); ++ otg_leave_state(fsm, fsm->transceiver->state); ++ switch (new_state) { ++ case OTG_STATE_B_IDLE: ++ otg_drv_vbus(fsm, 0); ++ otg_chrg_vbus(fsm, 0); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_UNDEF); ++ otg_add_timer(fsm, b_se0_srp_tmr); ++ break; ++ case OTG_STATE_B_SRP_INIT: ++ otg_start_pulse(fsm); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_UNDEF); ++ otg_add_timer(fsm, b_srp_fail_tmr); ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ otg_chrg_vbus(fsm, 0); ++ otg_loc_conn(fsm, 1); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_GADGET); ++ break; ++ case OTG_STATE_B_WAIT_ACON: ++ otg_chrg_vbus(fsm, 0); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_HOST); ++ otg_add_timer(fsm, b_ase0_brst_tmr); ++ fsm->a_bus_suspend = 0; ++ break; ++ case OTG_STATE_B_HOST: ++ otg_chrg_vbus(fsm, 0); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 1); ++ otg_set_protocol(fsm, PROTO_HOST); ++ usb_bus_start_enum(fsm->transceiver->host, ++ fsm->transceiver->host->otg_port); ++ break; ++ case OTG_STATE_A_IDLE: ++ otg_drv_vbus(fsm, 0); ++ otg_chrg_vbus(fsm, 0); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_HOST); ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ otg_drv_vbus(fsm, 1); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_HOST); ++ otg_add_timer(fsm, a_wait_vrise_tmr); ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ otg_drv_vbus(fsm, 1); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_HOST); ++ otg_add_timer(fsm, a_wait_bcon_tmr); ++ break; ++ case OTG_STATE_A_HOST: ++ otg_drv_vbus(fsm, 1); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 1); ++ otg_set_protocol(fsm, PROTO_HOST); ++ /* When HNP is triggered while a_bus_req = 0, a_host will ++ * suspend too fast to complete a_set_b_hnp_en */ ++ if (!fsm->a_bus_req || fsm->a_suspend_req) ++ otg_add_timer(fsm, a_wait_enum_tmr); ++ break; ++ case OTG_STATE_A_SUSPEND: ++ otg_drv_vbus(fsm, 1); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_HOST); ++ otg_add_timer(fsm, a_aidl_bdis_tmr); ++ ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ otg_loc_conn(fsm, 1); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_GADGET); ++ otg_drv_vbus(fsm, 1); ++ break; ++ case OTG_STATE_A_WAIT_VFALL: ++ otg_drv_vbus(fsm, 0); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_HOST); ++ break; ++ case OTG_STATE_A_VBUS_ERR: ++ otg_drv_vbus(fsm, 0); ++ otg_loc_conn(fsm, 0); ++ otg_loc_sof(fsm, 0); ++ otg_set_protocol(fsm, PROTO_UNDEF); ++ break; ++ default: ++ break; ++ } ++ ++ fsm->transceiver->state = new_state; ++ return 0; ++} ++ ++/* State change judgement */ ++int otg_statemachine(struct otg_fsm *fsm) ++{ ++ enum usb_otg_state state; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fsm->lock, flags); ++ ++ state = fsm->transceiver->state; ++ state_changed = 0; ++ /* State machine state change judgement */ ++ ++ switch (state) { ++ case OTG_STATE_UNDEFINED: ++ VDBG("fsm->id = %d\n", fsm->id); ++ if (fsm->id) ++ otg_set_state(fsm, OTG_STATE_B_IDLE); ++ else ++ otg_set_state(fsm, OTG_STATE_A_IDLE); ++ break; ++ case OTG_STATE_B_IDLE: ++ if (!fsm->id) ++ otg_set_state(fsm, OTG_STATE_A_IDLE); ++ else if (fsm->b_sess_vld && fsm->transceiver->gadget) ++ otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); ++ else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) ++ otg_set_state(fsm, OTG_STATE_B_SRP_INIT); ++ break; ++ case OTG_STATE_B_SRP_INIT: ++ if (!fsm->id || fsm->b_srp_done) ++ otg_set_state(fsm, OTG_STATE_B_IDLE); ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (!fsm->id || !fsm->b_sess_vld) ++ otg_set_state(fsm, OTG_STATE_B_IDLE); ++ else if (fsm->b_bus_req && fsm->transceiver-> ++ gadget->b_hnp_enable && fsm->a_bus_suspend) ++ otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); ++ break; ++ case OTG_STATE_B_WAIT_ACON: ++ if (fsm->a_conn) ++ otg_set_state(fsm, OTG_STATE_B_HOST); ++ else if (!fsm->id || !fsm->b_sess_vld) ++ otg_set_state(fsm, OTG_STATE_B_IDLE); ++ else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { ++ fsm->b_ase0_brst_tmout = 0; ++ otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); ++ } ++ break; ++ case OTG_STATE_B_HOST: ++ if (!fsm->id || !fsm->b_sess_vld) ++ otg_set_state(fsm, OTG_STATE_B_IDLE); ++ else if (!fsm->b_bus_req || !fsm->a_conn) ++ otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); ++ break; ++ case OTG_STATE_A_IDLE: ++ if (fsm->id) ++ otg_set_state(fsm, OTG_STATE_B_IDLE); ++ else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); ++ break; ++ case OTG_STATE_A_WAIT_VRISE: ++ if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || ++ fsm->a_wait_vrise_tmout) { ++ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); ++ } ++ break; ++ case OTG_STATE_A_WAIT_BCON: ++ if (!fsm->a_vbus_vld) ++ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); ++ else if (fsm->b_conn) ++ otg_set_state(fsm, OTG_STATE_A_HOST); ++ else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); ++ break; ++ case OTG_STATE_A_HOST: ++ if ((!fsm->a_bus_req || fsm->a_suspend_req) && ++ fsm->transceiver->host->b_hnp_enable) ++ otg_set_state(fsm, OTG_STATE_A_SUSPEND); ++ else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); ++ else if (!fsm->a_vbus_vld) ++ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); ++ break; ++ case OTG_STATE_A_SUSPEND: ++ if (!fsm->b_conn && fsm->transceiver->host->b_hnp_enable) ++ otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); ++ else if (!fsm->b_conn && !fsm->transceiver->host->b_hnp_enable) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); ++ else if (fsm->a_bus_req || fsm->b_bus_resume) ++ otg_set_state(fsm, OTG_STATE_A_HOST); ++ else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); ++ else if (!fsm->a_vbus_vld) ++ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); ++ break; ++ case OTG_STATE_A_PERIPHERAL: ++ if (fsm->id || fsm->a_bus_drop) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); ++ else if (fsm->b_bus_suspend) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); ++ else if (!fsm->a_vbus_vld) ++ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); ++ break; ++ case OTG_STATE_A_WAIT_VFALL: ++ if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && ++ !fsm->b_conn)) ++ otg_set_state(fsm, OTG_STATE_A_IDLE); ++ break; ++ case OTG_STATE_A_VBUS_ERR: ++ if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) ++ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); ++ break; ++ default: ++ break; ++ } ++ spin_unlock_irqrestore(&fsm->lock, flags); ++ ++ /*VDBG("quit statemachine, changed = %d\n", state_changed); */ ++ return state_changed; ++} +--- /dev/null ++++ b/drivers/usb/otg/otg_fsm.h +@@ -0,0 +1,151 @@ ++/* Copyright 2006-2011 Freescale Semiconductor, Inc. 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#if 0 ++#define DEBUG 1 ++#define VERBOSE 1 ++#endif ++ ++#ifdef DEBUG ++#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ ++ __func__, ## args) ++#else ++#define DBG(fmt, args...) do {} while (0) ++#endif ++ ++#ifdef VERBOSE ++#define VDBG DBG ++#else ++#define VDBG(stuff...) do {} while (0) ++#endif ++ ++#ifdef VERBOSE ++#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) ++#else ++#define MPC_LOC do {} while (0) ++#endif ++ ++#define PROTO_UNDEF (0) ++#define PROTO_HOST (1) ++#define PROTO_GADGET (2) ++ ++/* OTG state machine according to the OTG spec */ ++struct otg_fsm { ++ /* Input */ ++ int a_bus_resume; ++ int a_bus_suspend; ++ int a_conn; ++ int a_sess_vld; ++ int a_srp_det; ++ int a_vbus_vld; ++ int b_bus_resume; ++ int b_bus_suspend; ++ int b_conn; ++ int b_se0_srp; ++ int b_sess_end; ++ int b_sess_vld; ++ int id; ++ ++ /* Internal variables */ ++ int a_set_b_hnp_en; ++ int b_srp_done; ++ int b_hnp_enable; ++ ++ /* Timeout indicator for timers */ ++ int a_wait_vrise_tmout; ++ int a_wait_bcon_tmout; ++ int a_aidl_bdis_tmout; ++ int b_ase0_brst_tmout; ++ ++ /* Informative variables */ ++ int a_bus_drop; ++ int a_bus_req; ++ int a_clr_err; ++ int a_suspend_req; ++ int b_bus_req; ++ ++ /* Output */ ++ int drv_vbus; ++ int loc_conn; ++ int loc_sof; ++ ++ struct otg_fsm_ops *ops; ++ struct otg_transceiver *transceiver; ++ ++ /* Current usb protocol used: 0:undefine; 1:host; 2:client */ ++ int protocol; ++ spinlock_t lock; ++}; ++ ++struct otg_fsm_ops { ++ void (*chrg_vbus)(int on); ++ void (*drv_vbus)(int on); ++ void (*loc_conn)(int on); ++ void (*loc_sof)(int on); ++ void (*start_pulse)(void); ++ void (*add_timer)(void *timer); ++ void (*del_timer)(void *timer); ++ int (*start_host)(struct otg_fsm *fsm, int on); ++ int (*start_gadget)(struct otg_fsm *fsm, int on); ++}; ++ ++ ++static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) ++{ ++ fsm->ops->chrg_vbus(on); ++} ++ ++static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) ++{ ++ if (fsm->drv_vbus != on) { ++ fsm->drv_vbus = on; ++ fsm->ops->drv_vbus(on); ++ } ++} ++ ++static inline void otg_loc_conn(struct otg_fsm *fsm, int on) ++{ ++ if (fsm->loc_conn != on) { ++ fsm->loc_conn = on; ++ fsm->ops->loc_conn(on); ++ } ++} ++ ++static inline void otg_loc_sof(struct otg_fsm *fsm, int on) ++{ ++ if (fsm->loc_sof != on) { ++ fsm->loc_sof = on; ++ fsm->ops->loc_sof(on); ++ } ++} ++ ++static inline void otg_start_pulse(struct otg_fsm *fsm) ++{ ++ fsm->ops->start_pulse(); ++} ++ ++static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) ++{ ++ fsm->ops->add_timer(timer); ++} ++ ++static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) ++{ ++ fsm->ops->del_timer(timer); ++} ++ ++int otg_statemachine(struct otg_fsm *fsm); +--- /dev/null ++++ b/drivers/usb/otg/usb.c +@@ -0,0 +1,76 @@ ++/* Copyright 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Author: Li Yang <LeoLi@freescale.com> ++ * Jerry Huang <Chang-Ming.Huang@freescale.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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/usb/otg.h> ++ ++#include <linux/io.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++ ++ ++static struct otg_transceiver *transceiver; ++static struct resource *otg_resources; ++ ++/* ++ * otg_get_transceiver - find the (single) OTG transceiver driver ++ * ++ * Returns the transceiver driver, after getting a refcount to it; or ++ * null if there is no such transceiver. The caller is responsible for ++ * releasing that count. ++ */ ++struct otg_transceiver *otg_get_transceiver(void) ++{ ++ pr_debug("%s transceiver=0x%p\n", __func__, transceiver); ++ if (transceiver) ++ get_device(transceiver->dev); ++ ++ return transceiver; ++} ++EXPORT_SYMBOL(otg_get_transceiver); ++ ++int otg_set_transceiver(struct otg_transceiver *otg) ++{ ++ if (transceiver && otg) ++ return -EBUSY; ++ ++ transceiver = otg; ++ ++ return 0; ++} ++EXPORT_SYMBOL(otg_set_transceiver); ++ ++struct resource *otg_get_resources(void) ++{ ++ return otg_resources; ++} ++EXPORT_SYMBOL(otg_get_resources); ++ ++int otg_set_resources(struct resource *resources) ++{ ++ otg_resources = resources; ++ return 0; ++} ++EXPORT_SYMBOL(otg_set_resources); +--- a/include/linux/fsl_devices.h ++++ b/include/linux/fsl_devices.h +@@ -72,6 +72,18 @@ struct fsl_usb2_platform_data { + void (*exit)(struct platform_device *); + void __iomem *regs; /* ioremap'd register base */ + struct clk *clk; ++#ifdef CONFIG_COLDFIRE ++ u32 xcvr_type; /* PORTSC_PTS_* */ ++ char *name; /* pretty print */ ++ char *transceiver; /* transceiver name */ ++ unsigned power_budget; /* for hcd->power_budget */ ++ struct platform_device *pdev; ++ struct fsl_xcvr_ops *xcvr_ops; ++ ++ int (*gpio_usb_active) (void); ++ void (*gpio_usb_inactive) (void); ++ ++#endif + unsigned big_endian_mmio:1; + unsigned big_endian_desc:1; + unsigned es:1; /* need USBMODE:ES */ +@@ -79,6 +91,20 @@ struct fsl_usb2_platform_data { + unsigned have_sysif_regs:1; + unsigned invert_drvvbus:1; + unsigned invert_pwr_fault:1; ++#ifdef CONFIG_COLDFIRE ++ unsigned suspended:1; ++ unsigned already_suspended:1; ++#endif ++ /* register save area for suspend/resume */ ++ u32 pm_command; ++ u32 pm_status; ++ u32 pm_intr_enable; ++ u32 pm_frame_index; ++ u32 pm_segment; ++ u32 pm_frame_list; ++ u32 pm_async_next; ++ u32 pm_configured_flag; ++ u32 pm_portsc; + }; + + /* Flags in fsl_usb2_mph_platform_data */ +--- /dev/null ++++ b/include/linux/usb/fsl_usb2.h +@@ -0,0 +1,410 @@ ++/* ++ * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Freescale USB device/endpoint management registers ++ * ++ * 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 __FSL_USB2_H ++#define __FSL_USB2_H ++ ++ ++ /* USB DR device mode registers (Little Endian) */ ++struct fsl_usb_device_regs { ++ /* Capability register */ ++ u32 id; ++ u32 res1[63]; ++ u16 caplength; /* Capability Register Length */ ++ u16 hciversion; /* Host Controller Interface Version */ ++ u32 hcsparams; /* Host Controller Structual Parameters */ ++ u32 hccparams; /* Host Controller Capability Parameters */ ++ u32 res2[5]; ++ u32 dciversion; /* Device Controller Interface Version */ ++ u32 dccparams; /* Device Controller Capability Parameters */ ++ u32 res3[6]; ++ /* Operation register */ ++ u32 usbcmd; /* USB Command Register */ ++ u32 usbsts; /* USB Status Register */ ++ u32 usbintr; /* USB Interrupt Enable Register */ ++ u32 frindex; /* Frame Index Register */ ++ u32 res4; ++ u32 deviceaddr; /* Device Address */ ++ u32 endpointlistaddr; /* Endpoint List Address Register */ ++ u32 res5; ++ u32 burstsize; /* Master Interface Data Burst Size Register */ ++ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ ++ u32 res6[2]; ++ u32 ulpiview; ++ u32 res7[3]; ++ u32 configflag; /* Configure Flag Register */ ++ u32 portsc1; /* Port 1 Status and Control Register */ ++ u32 res8[7]; ++ u32 otgsc; /* On-The-Go Status and Control */ ++ u32 usbmode; /* USB Mode Register */ ++ u32 endptsetupstat; /* Endpoint Setup Status Register */ ++ u32 endpointprime; /* Endpoint Initialization Register */ ++ u32 endptflush; /* Endpoint Flush Register */ ++ u32 endptstatus; /* Endpoint Status Register */ ++ u32 endptcomplete; /* Endpoint Complete Register */ ++ /* DCCPARAMS:DEN has the real number of device endpoints. */ ++ u32 endptctrl[16]; /* Endpoint Control Registers */ ++}; ++ ++ /* USB DR host mode registers (Little Endian) */ ++struct fsl_usb_host_regs { ++ /* Capability register */ ++ u32 id; ++ u32 res1[63]; ++ u16 caplength; /* Capability Register Length */ ++ u16 hciversion; /* Host Controller Interface Version */ ++ u32 hcsparams; /* Host Controller Structual Parameters */ ++ u32 hccparams; /* Host Controller Capability Parameters */ ++ u32 res2[5]; ++ u32 dciversion; /* Device Controller Interface Version */ ++ u32 dccparams; /* Device Controller Capability Parameters */ ++ u32 res3[6]; ++ /* Operation register */ ++ u32 usbcmd; /* USB Command Register */ ++ u32 usbsts; /* USB Status Register */ ++ u32 usbintr; /* USB Interrupt Enable Register */ ++ u32 frindex; /* Frame Index Register */ ++ u32 res4; ++ u32 periodiclistbase; /* Periodic Frame List Base Address Register */ ++ u32 asynclistaddr; /* Current Asynchronous List Address Register */ ++ u32 res5; ++ u32 burstsize; /* Master Interface Data Burst Size Register */ ++ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ ++ u32 res6[2]; ++ u32 ulpiview; ++ u32 res7[3]; ++ u32 configflag; /* Configure Flag Register */ ++ u32 portsc1; /* Port 1 Status and Control Register */ ++ u32 res8[7]; ++ u32 otgsc; /* On-The-Go Status and Control */ ++ u32 usbmode; /* USB Mode Register */ ++ u32 endptsetupstat; /* Endpoint Setup Status Register */ ++ u32 endpointprime; /* Endpoint Initialization Register */ ++ u32 endptflush; /* Endpoint Flush Register */ ++ u32 endptstatus; /* Endpoint Status Register */ ++ u32 endptcomplete; /* Endpoint Complete Register */ ++ /* DCCPARAMS:DEN has the real number of device endpoints. */ ++ u32 endptctrl[16]; /* Endpoint Control Registers */ ++}; ++ ++ /* non-EHCI USB system interface registers (Big Endian) */ ++struct usb_sys_interface { ++ u32 snoop1; ++ u32 snoop2; ++ u32 age_cnt_thresh; /* Age Count Threshold Register */ ++ u32 pri_ctrl; /* Priority Control Register */ ++ u32 si_ctrl; /* System Interface Control Register */ ++ u32 res[59]; ++ u32 control; /* General Purpose Control Register */ ++}; ++ ++/* ep0 transfer state */ ++#define WAIT_FOR_SETUP 0 ++#define DATA_STATE_XMIT 1 ++#define DATA_STATE_NEED_ZLP 2 ++#define WAIT_FOR_OUT_STATUS 3 ++#define DATA_STATE_RECV 4 ++ ++/* Frame Index Register Bit Masks */ ++#define USB_FRINDEX_MASKS 0x3fff ++ ++ ++ ++/* USBCMD Register Bit Masks */ ++#define USB_CMD_RUN_STOP 0x00000001 ++#define USB_CMD_CTRL_RESET 0x00000002 ++#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 ++#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 ++#define USB_CMD_INT_AA_DOORBELL 0x00000040 ++#define USB_CMD_ASP 0x00000300 ++#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 ++#define USB_CMD_SUTW 0x00002000 ++#define USB_CMD_ATDTW 0x00004000 ++#define USB_CMD_ITC 0x00FF0000 ++ ++/* bit 15,3,2 are frame list size */ ++#define USB_CMD_FRAME_SIZE_1024 0x00000000 ++#define USB_CMD_FRAME_SIZE_512 0x00000004 ++#define USB_CMD_FRAME_SIZE_256 0x00000008 ++#define USB_CMD_FRAME_SIZE_128 0x0000000C ++#define USB_CMD_FRAME_SIZE_64 0x00008000 ++#define USB_CMD_FRAME_SIZE_32 0x00008004 ++#define USB_CMD_FRAME_SIZE_16 0x00008008 ++#define USB_CMD_FRAME_SIZE_8 0x0000800C ++ ++/* bit 9-8 are async schedule park mode count */ ++#define USB_CMD_ASP_00 0x00000000 ++#define USB_CMD_ASP_01 0x00000100 ++#define USB_CMD_ASP_10 0x00000200 ++#define USB_CMD_ASP_11 0x00000300 ++#define USB_CMD_ASP_BIT_POS 8 ++ ++/* bit 23-16 are interrupt threshold control */ ++#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 ++#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 ++#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 ++#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 ++#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 ++#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 ++#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 ++#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 ++#define USB_CMD_ITC_BIT_POS 16 ++ ++ ++ ++ ++/* USB STS Register Bit Masks */ ++#define USB_STS_INT 0x00000001 ++#define USB_STS_ERR 0x00000002 ++#define USB_STS_PORT_CHANGE 0x00000004 ++#define USB_STS_FRM_LST_ROLL 0x00000008 ++#define USB_STS_SYS_ERR 0x00000010 ++#define USB_STS_IAA 0x00000020 ++#define USB_STS_RESET 0x00000040 ++#define USB_STS_SOF 0x00000080 ++#define USB_STS_SUSPEND 0x00000100 ++#define USB_STS_HC_HALTED 0x00001000 ++#define USB_STS_RCL 0x00002000 ++#define USB_STS_PERIODIC_SCHEDULE 0x00004000 ++#define USB_STS_ASYNC_SCHEDULE 0x00008000 ++ ++/* USB INTR Register Bit Masks */ ++#define USB_INTR_INT_EN 0x00000001 ++#define USB_INTR_ERR_INT_EN 0x00000002 ++#define USB_INTR_PTC_DETECT_EN 0x00000004 ++#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 ++#define USB_INTR_SYS_ERR_EN 0x00000010 ++#define USB_INTR_ASYN_ADV_EN 0x00000020 ++#define USB_INTR_RESET_EN 0x00000040 ++#define USB_INTR_SOF_EN 0x00000080 ++#define USB_INTR_DEVICE_SUSPEND 0x00000100 ++ ++/* Device Address bit masks */ ++#define USB_DEVICE_ADDRESS_MASK 0xFE000000 ++#define USB_DEVICE_ADDRESS_BIT_POS 25 ++ ++/* endpoint list address bit masks */ ++#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 ++ ++ ++/* x_PORTSCx */ ++/* bit 31-30 are port transceiver select */ ++#define PORTSCX_PTS_MASK (3 << 30) /* parallel xcvr select mask */ ++#define PORTSCX_PHY_TYPE_SEL PORTSCX_PTS_MASK ++#define PORTSCX_PTS_UTMI (0 << 30) /* UTMI/UTMI+ */ ++#define PORTSCX_PTS_PHILIPS (1 << 30) /* Philips classic */ ++#define PORTSCX_PTS_ULPI (2 << 30) /* ULPI */ ++#define PORTSCX_PTS_SERIAL (3 << 30) /* serial */ ++#define PORTSCX_PTS_FSLS PORTSCX_PTS_SERIAL ++#define PORTSCX_PTS_ONCHIP PORTSCX_PTS_FSLS ++ ++#define PORTSCX_STS (1 << 29) /* serial xcvr select */ ++ ++/* bit 28 is parallel transceiver width for UTMI interface */ ++#define PORTSCX_PTW_8BIT (0 << 28) /* 8 bit parallel xcvr */ ++#define PORTSCX_PTW_16BIT (1 << 28) /* 16 bi parallel xcvr */ ++ ++/* bit 27-26 are port speed */ ++#define PORTSCX_PORT_SPEED_FULL (0 << 26) ++#define PORTSCX_PORT_SPEED_LOW (1 << 26) ++#define PORTSCX_PORT_SPEED_HIGH (2 << 26) ++#define PORTSCX_PORT_SPEED_UNDEF (3 << 26) ++#define PORTSCX_PORT_SPEED_MASK (3 << 26) ++ ++/* phy low pwr suspend/clk disable */ ++#define PORTSCX_PHY_LOW_POWER_SPD (1 << 23) ++ ++/* bit 19-16 are port test control */ ++#define PORTSCX_PTC_DISABLE (0 << 16) ++#define PORTSCX_PTC_JSTATE (1 << 16) ++#define PORTSCX_PTC_KSTATE (2 << 16) ++#define PORTSCX_PTC_SEQNAK (3 << 16) /* SE0 (host)/NAK (device)*/ ++#define PORTSCX_PTC_PACKET (4 << 16) ++#define PORTSCX_PTC_FORCE_EN_HS (5 << 16) ++#define PORTSCX_PTC_FORCE_EN_FS (6 << 16) ++#define PORTSCX_PTC_FORCE_EN_LS (7 << 16) ++ ++ ++/* bit 15-14 are port indicator control */ ++#define PORTSCX_PIC_OFF (0 << 14) ++#define PORTSCX_PIC_AMBER (1 << 14) ++#define PORTSCX_PIC_GREEN (2 << 14) ++#define PORTSCX_PIC_UNDEF (3 << 14) ++ ++#define PORTSCX_PORT_POWER (1 << 12) /* port power */ ++ ++/* bit 11-10 are line status */ ++#define PORTSCX_LS_MASK (3 << 10) /* Line State mask */ ++#define PORTSCX_LS_SE0 (0 << 10) /* SE0 */ ++#define PORTSCX_LS_K_STATE (1 << 10) /* K-state */ ++#define PORTSCX_LS_J_STATE (2 << 10) /* J-state */ ++ ++#define PORTSCX_PORT_RESET (1 << 8) /* Port reset */ ++#define PORTSCX_PORT_SUSPEND (1 << 7) /* Suspend */ ++#define PORTSCX_PORT_FORCE_RESUME (1 << 6) /* Force port resume */ ++#define PORTSCX_OVER_CURRENT_CHG (1 << 5) /* over current change */ ++#define PORTSCX_OVER_CURRENT_ACT (1 << 4) /* over currrent active */ ++#define PORTSCX_PORT_EN_DIS_CHANGE (1 << 3) /* port {en,dis}able change*/ ++#define PORTSCX_PORT_ENABLE (1 << 2) /* port enabled */ ++#define PORTSCX_CONNECT_STATUS_CHANGE (1 << 1) /* connect status change */ ++#define PORTSCX_CURRENT_CONNECT_STATUS (1 << 0) /* current connect status */ ++ ++#define PORTSCX_W1C_BITS \ ++ (PORTSCX_CONNECT_STATUS_CHANGE | \ ++ PORTSCX_PORT_EN_DIS_CHANGE | \ ++ PORTSCX_OVER_CURRENT_CHG) ++ ++ ++ ++/* UOG_OTGSC Register Bits */ ++/* control bits: */ ++#define OTGSC_CTRL_VBUS_DISCHARGE (1 << 0) ++#define OTGSC_CTRL_VBUS_CHARGE (1 << 1) ++#define OTGSC_CTRL_OTG_TERM (1 << 3)/* controls DM pulldown */ ++#define OTGSC_CTRL_DATA_PULSING (1 << 4) ++#define OTGSC_CTRL_USB_ID_PU (1 << 5) /* enable ID pullup */ ++/* current status: (R/O) */ ++#define OTGSC_STS_USB_ID (1 << 8)/* 0=A-device 1=B-device */ ++#define OTGSC_STS_A_VBUS_VALID (1 << 9) ++#define OTGSC_STS_A_SESSION_VALID (1 << 10) ++#define OTGSC_STS_B_SESSION_VALID (1 << 11) ++#define OTGSC_STS_B_SESSION_END (1 << 12) ++#define OTGSC_STS_1ms_TIMER (1 << 13) ++#define OTGSC_STS_DATA_PULSE (1 << 14) ++/* interrupt status: (write to clear) */ ++#define OTGSC_INTSTS_MASK (0x7f << 16) ++#define OTGSC_INTSTS_USB_ID (1 << 16) ++#define OTGSC_INTSTS_A_VBUS_VALID (1 << 17) ++#define OTGSC_INTSTS_A_SESSION_VALID (1 << 18) ++#define OTGSC_INTSTS_B_SESSION_VALID (1 << 19) ++#define OTGSC_INTSTS_B_SESSION_END (1 << 20) ++#define OTGSC_INTSTS_1MS_TIMER (1 << 21) ++#define OTGSC_INTSTS_DATA_PULSE (1 << 22) ++/* interrupt enables: */ ++#define OTGSC_IE_MASK (0x7f << 24) ++#define OTGSC_IE_USB_ID (1 << 24) ++#define OTGSC_IE_A_VBUS_VALID (1 << 25) ++#define OTGSC_IE_A_SESSION_VALID (1 << 26) ++#define OTGSC_IE_B_SESSION_VALID (1 << 27) ++#define OTGSC_IE_B_SESSION_END (1 << 28) ++#define OTGSC_IE_1ms_TIMER (1 << 29) ++#define OTGSC_IE_DATA_PULSE (1 << 30) ++ ++#if 1 /* DDD FIXME these here for compatibility between my names and Leo's */ ++/* OTG interrupt enable bit masks */ ++#define OTGSC_INTERRUPT_ENABLE_BITS_MASK OTGSC_IE_MASK ++ ++/* OTG interrupt status bit masks */ ++#define OTGSC_INTERRUPT_STATUS_BITS_MASK OTGSC_INTSTS_MASK ++#endif ++ ++ ++ ++/* x_USBMODE */ ++#undef USBMODE_SDIS /* defined as bit 3 in drivers/usb/host/ehci.h */ ++#define USBMODE_SDIS (1 << 4) /* stream disable mode */ ++#define USBMODE_SLOM (1 << 3) /* setup lockout mode */ ++#define USBMODE_ES (1 << 2) /* (big) endian select */ ++#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ ++#define USBMODE_CM_HOST (3 << 0) /* host */ ++#define USBMODE_CM_DEVICE (2 << 0) /* device */ ++ ++/* DDD for compatibility for now */ ++#define USB_MODE_CTRL_MODE_IDLE USBMODE_CM_IDLE ++#define USB_MODE_CTRL_MODE_DEVICE USBMODE_CM_DEVICE ++#define USB_MODE_CTRL_MODE_HOST USBMODE_CM_HOST ++#define USB_MODE_SETUP_LOCK_OFF USBMODE_SLOM ++#define USB_MODE_STREAM_DISABLE USBMODE_SDIS ++ ++ ++/* ULPIVIEW register bits */ ++#define ULPIVW_WU (1 << 31) /* Wakeup */ ++#define ULPIVW_RUN (1 << 30) /* read/write run */ ++#define ULPIVW_WRITE (1 << 29) /* 0=read 1=write */ ++#define ULPIVW_SS (1 << 27) /* SyncState */ ++#define ULPIVW_PORT_MASK 0x07 /* Port field */ ++#define ULPIVW_PORT_SHIFT 24 ++#define ULPIVW_ADDR_MASK 0xFF /* data address field */ ++#define ULPIVW_ADDR_SHIFT 16 ++#define ULPIVW_RDATA_MASK 0xFF /* read data field */ ++#define ULPIVW_RDATA_SHIFT 8 ++#define ULPIVW_WDATA_MASK 0xFF /* write data field */ ++#define ULPIVW_WDATA_SHIFT 0 ++ ++ ++/* Endpoint Flush Register */ ++#define EPFLUSH_TX_OFFSET 0x00010000 ++#define EPFLUSH_RX_OFFSET 0x00000000 ++ ++/* Endpoint Setup Status bit masks */ ++#define EP_SETUP_STATUS_MASK 0x0000003F ++#define EP_SETUP_STATUS_EP0 0x00000001 ++ ++/* ENDPOINTCTRLx Register Bit Masks */ ++#define EPCTRL_TX_ENABLE 0x00800000 ++#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ ++#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ ++#define EPCTRL_TX_TYPE 0x000C0000 ++#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ ++#define EPCTRL_TX_EP_STALL 0x00010000 ++#define EPCTRL_RX_ENABLE 0x00000080 ++#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ ++#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ ++#define EPCTRL_RX_TYPE 0x0000000C ++#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ ++#define EPCTRL_RX_EP_STALL 0x00000001 ++ ++/* bit 19-18 and 3-2 are endpoint type */ ++#define EPCTRL_EP_TYPE_CONTROL 0 ++#define EPCTRL_EP_TYPE_ISO 1 ++#define EPCTRL_EP_TYPE_BULK 2 ++#define EPCTRL_EP_TYPE_INTERRUPT 3 ++#define EPCTRL_TX_EP_TYPE_SHIFT 18 ++#define EPCTRL_RX_EP_TYPE_SHIFT 2 ++ ++/* pri_ctrl Register Bit Masks */ ++#define PRI_CTRL_PRI_LVL1 0x0000000C ++#define PRI_CTRL_PRI_LVL0 0x00000003 ++ ++/* si_ctrl Register Bit Masks */ ++#define SI_CTRL_ERR_DISABLE 0x00000010 ++#define SI_CTRL_IDRC_DISABLE 0x00000008 ++#define SI_CTRL_RD_SAFE_EN 0x00000004 ++#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002 ++#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001 ++ ++ ++/* control Register Bit Masks */ ++#define USB_CTRL_IOENB 0x00000004 ++#define USB_CTRL_ULPI_INT0EN 0x00000001 ++ ++ ++/* Endpoint Transfer Descriptor bit Masks */ ++#define DTD_NEXT_TERMINATE 0x00000001 ++#define DTD_IOC 0x00008000 ++#define DTD_STATUS_ACTIVE 0x00000080 ++#define DTD_STATUS_HALTED 0x00000040 ++#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 ++#define DTD_STATUS_TRANSACTION_ERR 0x00000008 ++#define DTD_RESERVED_FIELDS 0x80007300 ++#define DTD_ADDR_MASK 0xFFFFFFE0 ++#define DTD_PACKET_SIZE 0x7FFF0000 ++#define DTD_LENGTH_BIT_POS 16 ++#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ ++ DTD_STATUS_DATA_BUFF_ERR | \ ++ DTD_STATUS_TRANSACTION_ERR) ++/* Alignment requirements; must be a power of two */ ++#define DTD_ALIGNMENT 0x20 ++#define QH_ALIGNMENT 2048 ++ ++/* Controller dma boundary */ ++#define UDC_DMA_BOUNDARY 0x1000 ++ ++#endif /* __FSL_USB2_H */ +--- /dev/null ++++ b/include/linux/usb/fsl_xcvr.h +@@ -0,0 +1,36 @@ ++/* ++ * Copyright 2007-2011 Freescale Semiconductor, Inc. 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. ++ */ ++ ++/** ++ * struct fsl_xcvr_ops - USB transceiver operations ++ * ++ * @xcvr_type: one of PORTSCX_PTS_{UTMI,SERIAL,ULPI} ++ * @init: transceiver- and board-specific initialization function ++ * @uninit: transceiver- and board-specific uninitialization function ++ * @set_host: ++ * @set_device: ++ * ++ */ ++struct fsl_xcvr_ops { ++ char *name; ++ u32 xcvr_type; ++ struct fsl_usb2_platform_data *pdata; ++ ++ void (*init)(struct fsl_xcvr_ops *ops); ++ void (*uninit)(struct fsl_xcvr_ops *ops); ++ void (*suspend)(struct fsl_xcvr_ops *ops); ++ void (*set_host)(void); ++ void (*set_device)(void); ++ void (*set_vbus_power)(struct fsl_xcvr_ops *ops, int on); ++ void (*set_remote_wakeup)(u32 *view); ++ void (*pullup)(int on); ++}; ++ ++extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops); ++extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops); +--- a/include/linux/usb/otg.h ++++ b/include/linux/usb/otg.h +@@ -164,7 +164,7 @@ otg_shutdown(struct otg_transceiver *otg + } + + /* for usb host and peripheral controller drivers */ +-#ifdef CONFIG_USB_OTG_UTILS ++#if defined(CONFIG_USB_OTG_UTILS) || defined(CONFIG_COLDFIRE) + extern struct otg_transceiver *otg_get_transceiver(void); + extern void otg_put_transceiver(struct otg_transceiver *); + #else |