From 632dc9e46a9ff032138f81fd420c0f01fd6c3cbc Mon Sep 17 00:00:00 2001 From: Jason Jin 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 --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +/* 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 + +/* 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 +#include +#include + +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 * Jiang Bo @@ -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 +#include +#include +#include + +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 and * Anton Vorontsov . */ - #include #include #include #include #include #include - +#include #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 +#include +#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 +/* + * 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 + * Jerry Huang + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 + +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 +#include + +#ifdef CONFIG_PPC32 +#include +#elif CONFIG_COLDFIRE +#include +#include +#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 + * Jerry Huang + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#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 + * Jerry Huang + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +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