avionic design with actual uboot and tooling

submodule of avionic design uboot bootloader and with included tools to
get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
#
# MUSB Controller Driver
#
comment "MUSB Controller Driver"
config USB_MUSB_HOST
bool "MUSB host mode support"
help
Enables the MUSB USB dual-role controller in host mode.
config USB_MUSB_GADGET
bool "MUSB gadget mode support"
select USB_GADGET_DUALSPEED
help
Enables the MUSB USB dual-role controller in gadget mode.
if USB_MUSB_HOST || USB_MUSB_GADGET
config USB_MUSB_PIC32
bool "Enable Microchip PIC32 DRC USB controller"
depends on DM_USB && MACH_PIC32
help
Say y to enable PIC32 USB DRC controller support
if it is available on your Microchip PIC32 platform.
config USB_MUSB_SUNXI
bool "Enable sunxi OTG / DRC USB controller"
depends on ARCH_SUNXI
default y
---help---
Say y here to enable support for the sunxi OTG / DRC USB controller
used on almost all sunxi boards.
endif

View File

@@ -0,0 +1,18 @@
#
# for USB OTG silicon based on Mentor Graphics INVENTRA designs
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_USB_MUSB_GADGET) += musb_gadget.o musb_gadget_ep0.o musb_core.o
obj-$(CONFIG_USB_MUSB_GADGET) += musb_uboot.o
obj-$(CONFIG_USB_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o
obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o
obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o
obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o
obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
ccflags-y := $(call cc-option,-Wno-unused-variable) \
$(call cc-option,-Wno-unused-but-set-variable) \
$(call cc-option,-Wno-unused-label)

View File

@@ -0,0 +1,701 @@
/*
* Texas Instruments AM35x "glue layer"
*
* Copyright (c) 2010, by Texas Instruments
*
* Based on the DA8xx "glue layer" code.
* Copyright (c) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
*
* This file is part of the Inventra Controller Driver for Linux.
*
* SPDX-License-Identifier: GPL-2.0
*
*/
#ifndef __UBOOT__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <plat/usb.h>
#else
#include <common.h>
#include <asm/omap_musb.h>
#include "linux-compat.h"
#endif
#include "musb_core.h"
/*
* AM35x specific definitions
*/
/* USB 2.0 OTG module registers */
#define USB_REVISION_REG 0x00
#define USB_CTRL_REG 0x04
#define USB_STAT_REG 0x08
#define USB_EMULATION_REG 0x0c
/* 0x10 Reserved */
#define USB_AUTOREQ_REG 0x14
#define USB_SRP_FIX_TIME_REG 0x18
#define USB_TEARDOWN_REG 0x1c
#define EP_INTR_SRC_REG 0x20
#define EP_INTR_SRC_SET_REG 0x24
#define EP_INTR_SRC_CLEAR_REG 0x28
#define EP_INTR_MASK_REG 0x2c
#define EP_INTR_MASK_SET_REG 0x30
#define EP_INTR_MASK_CLEAR_REG 0x34
#define EP_INTR_SRC_MASKED_REG 0x38
#define CORE_INTR_SRC_REG 0x40
#define CORE_INTR_SRC_SET_REG 0x44
#define CORE_INTR_SRC_CLEAR_REG 0x48
#define CORE_INTR_MASK_REG 0x4c
#define CORE_INTR_MASK_SET_REG 0x50
#define CORE_INTR_MASK_CLEAR_REG 0x54
#define CORE_INTR_SRC_MASKED_REG 0x58
/* 0x5c Reserved */
#define USB_END_OF_INTR_REG 0x60
/* Control register bits */
#define AM35X_SOFT_RESET_MASK 1
/* USB interrupt register bits */
#define AM35X_INTR_USB_SHIFT 16
#define AM35X_INTR_USB_MASK (0x1ff << AM35X_INTR_USB_SHIFT)
#define AM35X_INTR_DRVVBUS 0x100
#define AM35X_INTR_RX_SHIFT 16
#define AM35X_INTR_TX_SHIFT 0
#define AM35X_TX_EP_MASK 0xffff /* EP0 + 15 Tx EPs */
#define AM35X_RX_EP_MASK 0xfffe /* 15 Rx EPs */
#define AM35X_TX_INTR_MASK (AM35X_TX_EP_MASK << AM35X_INTR_TX_SHIFT)
#define AM35X_RX_INTR_MASK (AM35X_RX_EP_MASK << AM35X_INTR_RX_SHIFT)
#define USB_MENTOR_CORE_OFFSET 0x400
struct am35x_glue {
struct device *dev;
struct platform_device *musb;
struct clk *phy_clk;
struct clk *clk;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
/*
* am35x_musb_enable - enable interrupts
*/
#ifndef __UBOOT__
static void am35x_musb_enable(struct musb *musb)
#else
static int am35x_musb_enable(struct musb *musb)
#endif
{
void __iomem *reg_base = musb->ctrl_base;
u32 epmask;
/* Workaround: setup IRQs through both register sets. */
epmask = ((musb->epmask & AM35X_TX_EP_MASK) << AM35X_INTR_TX_SHIFT) |
((musb->epmask & AM35X_RX_EP_MASK) << AM35X_INTR_RX_SHIFT);
musb_writel(reg_base, EP_INTR_MASK_SET_REG, epmask);
musb_writel(reg_base, CORE_INTR_MASK_SET_REG, AM35X_INTR_USB_MASK);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
if (is_otg_enabled(musb))
musb_writel(reg_base, CORE_INTR_SRC_SET_REG,
AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT);
#ifdef __UBOOT__
return 0;
#endif
}
/*
* am35x_musb_disable - disable HDRC and flush interrupts
*/
static void am35x_musb_disable(struct musb *musb)
{
void __iomem *reg_base = musb->ctrl_base;
musb_writel(reg_base, CORE_INTR_MASK_CLEAR_REG, AM35X_INTR_USB_MASK);
musb_writel(reg_base, EP_INTR_MASK_CLEAR_REG,
AM35X_TX_INTR_MASK | AM35X_RX_INTR_MASK);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_writel(reg_base, USB_END_OF_INTR_REG, 0);
}
#ifndef __UBOOT__
#define portstate(stmt) stmt
static void am35x_musb_set_vbus(struct musb *musb, int is_on)
{
WARN_ON(is_on && is_peripheral_active(musb));
}
#define POLL_SECONDS 2
static struct timer_list otg_workaround;
static void otg_timer(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
void __iomem *mregs = musb->mregs;
u8 devctl;
unsigned long flags;
/*
* We poll because AM35x's won't expose several OTG-critical
* status change events (from the transceiver) otherwise.
*/
devctl = musb_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
otg_state_string(musb->xceiv->state));
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
case OTG_STATE_A_WAIT_BCON:
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
musb->xceiv->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
case OTG_STATE_A_WAIT_VFALL:
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
musb_writel(musb->ctrl_base, CORE_INTR_SRC_SET_REG,
MUSB_INTR_VBUSERROR << AM35X_INTR_USB_SHIFT);
break;
case OTG_STATE_B_IDLE:
if (!is_peripheral_enabled(musb))
break;
devctl = musb_readb(mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
else
musb->xceiv->state = OTG_STATE_A_IDLE;
break;
default:
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
if (!is_otg_enabled(musb))
return;
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || (musb->a_wait_bcon == 0 &&
musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
otg_state_string(musb->xceiv->state));
del_timer(&otg_workaround);
last_timer = jiffies;
return;
}
if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) {
dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n");
return;
}
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
otg_state_string(musb->xceiv->state),
jiffies_to_msecs(timeout - jiffies));
mod_timer(&otg_workaround, timeout);
}
#endif
static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
void __iomem *reg_base = musb->ctrl_base;
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
struct usb_otg *otg = musb->xceiv->otg;
#else
struct omap_musb_board_data *data =
(struct omap_musb_board_data *)musb->controller;
#endif
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
u32 epintr, usbintr;
#ifdef __UBOOT__
/*
* It seems that on AM35X interrupt registers can be updated
* before core registers. This confuses the code.
* As a workaround add a small delay here.
*/
udelay(10);
#endif
spin_lock_irqsave(&musb->lock, flags);
/* Get endpoint interrupts */
epintr = musb_readl(reg_base, EP_INTR_SRC_MASKED_REG);
if (epintr) {
musb_writel(reg_base, EP_INTR_SRC_CLEAR_REG, epintr);
musb->int_rx =
(epintr & AM35X_RX_INTR_MASK) >> AM35X_INTR_RX_SHIFT;
musb->int_tx =
(epintr & AM35X_TX_INTR_MASK) >> AM35X_INTR_TX_SHIFT;
}
/* Get usb core interrupts */
usbintr = musb_readl(reg_base, CORE_INTR_SRC_MASKED_REG);
if (!usbintr && !epintr)
goto eoi;
if (usbintr) {
musb_writel(reg_base, CORE_INTR_SRC_CLEAR_REG, usbintr);
musb->int_usb =
(usbintr & AM35X_INTR_USB_MASK) >> AM35X_INTR_USB_SHIFT;
}
#ifndef __UBOOT__
/*
* DRVVBUS IRQs are the only proxy we have (a very poor one!) for
* AM35x's missing ID change IRQ. We need an ID change IRQ to
* switch appropriately between halves of the OTG state machine.
* Managing DEVCTL.SESSION per Mentor docs requires that we know its
* value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
*/
if (usbintr & (AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT)) {
int drvvbus = musb_readl(reg_base, USB_STAT_REG);
void __iomem *mregs = musb->mregs;
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
int err;
err = is_host_enabled(musb) && (musb->int_usb &
MUSB_INTR_VBUSERROR);
if (err) {
/*
* The Mentor core doesn't debounce VBUS as needed
* to cope with device connect current spikes. This
* means it's not uncommon for bus-powered devices
* to get VBUS errors during enumeration.
*
* This is a workaround, but newer RTL from Mentor
* seems to allow a better one: "re"-starting sessions
* without waiting for VBUS to stop registering in
* devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (is_host_enabled(musb) && drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&otg_workaround);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
otg->default_a = 0;
musb->xceiv->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
/* NOTE: this must complete power-on within 100 ms. */
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
otg_state_string(musb->xceiv->state),
err ? " ERROR" : "",
devctl);
ret = IRQ_HANDLED;
}
#endif
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret |= musb_interrupt(musb);
eoi:
/* EOI needs to be written for the IRQ to be re-asserted. */
if (ret == IRQ_HANDLED || epintr || usbintr) {
/* clear level interrupt */
if (data->clear_irq)
data->clear_irq();
/* write EOI */
musb_writel(reg_base, USB_END_OF_INTR_REG, 0);
}
#ifndef __UBOOT__
/* Poll for ID change */
if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
#endif
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
#ifndef __UBOOT__
static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode)
{
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
int retval = 0;
if (data->set_mode)
data->set_mode(musb_mode);
else
retval = -EIO;
return retval;
}
#endif
static int am35x_musb_init(struct musb *musb)
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
#else
struct omap_musb_board_data *data =
(struct omap_musb_board_data *)musb->controller;
#endif
void __iomem *reg_base = musb->ctrl_base;
u32 rev;
musb->mregs += USB_MENTOR_CORE_OFFSET;
/* Returns zero if e.g. not clocked */
rev = musb_readl(reg_base, USB_REVISION_REG);
if (!rev)
return -ENODEV;
#ifndef __UBOOT__
usb_nop_xceiv_register();
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(musb->xceiv))
return -ENODEV;
if (is_host_enabled(musb))
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
#endif
/* Reset the musb */
if (data->reset)
data->reset();
/* Reset the controller */
musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK);
/* Start the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(1);
msleep(5);
musb->isr = am35x_musb_interrupt;
/* clear level interrupt */
if (data->clear_irq)
data->clear_irq();
return 0;
}
static int am35x_musb_exit(struct musb *musb)
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
#else
struct omap_musb_board_data *data =
(struct omap_musb_board_data *)musb->controller;
#endif
#ifndef __UBOOT__
if (is_host_enabled(musb))
del_timer_sync(&otg_workaround);
#endif
/* Shutdown the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(0);
#ifndef __UBOOT__
usb_put_phy(musb->xceiv);
usb_nop_xceiv_unregister();
#endif
return 0;
}
/* AM35x supports only 32bit read operation */
void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
void __iomem *fifo = hw_ep->fifo;
u32 val;
int i;
/* Read for 32bit-aligned destination address */
if (likely((0x03 & (unsigned long) dst) == 0) && len >= 4) {
readsl(fifo, dst, len >> 2);
dst += len & ~0x03;
len &= 0x03;
}
/*
* Now read the remaining 1 to 3 byte or complete length if
* unaligned address.
*/
if (len > 4) {
for (i = 0; i < (len >> 2); i++) {
*(u32 *) dst = musb_readl(fifo, 0);
dst += 4;
}
len &= 0x03;
}
if (len > 0) {
val = musb_readl(fifo, 0);
memcpy(dst, &val, len);
}
}
#ifndef __UBOOT__
static const struct musb_platform_ops am35x_ops = {
#else
const struct musb_platform_ops am35x_ops = {
#endif
.init = am35x_musb_init,
.exit = am35x_musb_exit,
.enable = am35x_musb_enable,
.disable = am35x_musb_disable,
#ifndef __UBOOT__
.set_mode = am35x_musb_set_mode,
.try_idle = am35x_musb_try_idle,
.set_vbus = am35x_musb_set_vbus,
#endif
};
#ifndef __UBOOT__
static u64 am35x_dmamask = DMA_BIT_MASK(32);
static int __devinit am35x_probe(struct platform_device *pdev)
{
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
struct platform_device *musb;
struct am35x_glue *glue;
struct clk *phy_clk;
struct clk *clk;
int ret = -ENOMEM;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
}
musb = platform_device_alloc("musb-hdrc", -1);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
goto err1;
}
phy_clk = clk_get(&pdev->dev, "fck");
if (IS_ERR(phy_clk)) {
dev_err(&pdev->dev, "failed to get PHY clock\n");
ret = PTR_ERR(phy_clk);
goto err2;
}
clk = clk_get(&pdev->dev, "ick");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
goto err3;
}
ret = clk_enable(phy_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable PHY clock\n");
goto err4;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
goto err5;
}
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &am35x_dmamask;
musb->dev.coherent_dma_mask = am35x_dmamask;
glue->dev = &pdev->dev;
glue->musb = musb;
glue->phy_clk = phy_clk;
glue->clk = clk;
pdata->platform_ops = &am35x_ops;
platform_set_drvdata(pdev, glue);
ret = platform_device_add_resources(musb, pdev->resource,
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
goto err6;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
goto err6;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
goto err6;
}
return 0;
err6:
clk_disable(clk);
err5:
clk_disable(phy_clk);
err4:
clk_put(clk);
err3:
clk_put(phy_clk);
err2:
platform_device_put(musb);
err1:
kfree(glue);
err0:
return ret;
}
static int __devexit am35x_remove(struct platform_device *pdev)
{
struct am35x_glue *glue = platform_get_drvdata(pdev);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
clk_disable(glue->clk);
clk_disable(glue->phy_clk);
clk_put(glue->clk);
clk_put(glue->phy_clk);
kfree(glue);
return 0;
}
#ifdef CONFIG_PM
static int am35x_suspend(struct device *dev)
{
struct am35x_glue *glue = dev_get_drvdata(dev);
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
/* Shutdown the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(0);
clk_disable(glue->phy_clk);
clk_disable(glue->clk);
return 0;
}
static int am35x_resume(struct device *dev)
{
struct am35x_glue *glue = dev_get_drvdata(dev);
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
int ret;
/* Start the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(1);
ret = clk_enable(glue->phy_clk);
if (ret) {
dev_err(dev, "failed to enable PHY clock\n");
return ret;
}
ret = clk_enable(glue->clk);
if (ret) {
dev_err(dev, "failed to enable clock\n");
return ret;
}
return 0;
}
static struct dev_pm_ops am35x_pm_ops = {
.suspend = am35x_suspend,
.resume = am35x_resume,
};
#define DEV_PM_OPS &am35x_pm_ops
#else
#define DEV_PM_OPS NULL
#endif
static struct platform_driver am35x_driver = {
.probe = am35x_probe,
.remove = __devexit_p(am35x_remove),
.driver = {
.name = "musb-am35x",
.pm = DEV_PM_OPS,
},
};
MODULE_DESCRIPTION("AM35x MUSB Glue Layer");
MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>");
MODULE_LICENSE("GPL v2");
static int __init am35x_init(void)
{
return platform_driver_register(&am35x_driver);
}
module_init(am35x_init);
static void __exit am35x_exit(void)
{
platform_driver_unregister(&am35x_driver);
}
module_exit(am35x_exit);
#endif

View File

@@ -0,0 +1,37 @@
#ifndef __LINUX_COMPAT_H__
#define __LINUX_COMPAT_H__
#include <malloc.h>
#include <linux/list.h>
#include <linux/compat.h>
#define pr_debug(fmt, args...) debug(fmt, ##args)
#define WARN(condition, fmt, args...) ({ \
int ret_warn = !!condition; \
if (ret_warn) \
printf(fmt, ##args); \
ret_warn; })
#define device_init_wakeup(dev, a) do {} while (0)
#define platform_data device_data
#ifndef wmb
#define wmb() asm volatile ("" : : : "memory")
#endif
#define msleep(a) udelay(a * 1000)
/*
* Map U-Boot config options to Linux ones
*/
#ifdef CONFIG_OMAP34XX
#define CONFIG_SOC_OMAP3430
#endif
#ifdef CONFIG_OMAP4430
#define CONFIG_ARCH_OMAP4
#endif
#endif /* __LINUX_COMPAT_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,617 @@
/*
* MUSB OTG driver defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_CORE_H__
#define __MUSB_CORE_H__
#ifndef __UBOOT__
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/device.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
#else
#include <asm/errno.h>
#endif
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/musb.h>
struct musb;
struct musb_hw_ep;
struct musb_ep;
/* Helper defines for struct musb->hwvers */
#define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f)
#define MUSB_HWVERS_MINOR(x) (x & 0x3ff)
#define MUSB_HWVERS_RC 0x8000
#define MUSB_HWVERS_1300 0x52C
#define MUSB_HWVERS_1400 0x590
#define MUSB_HWVERS_1800 0x720
#define MUSB_HWVERS_1900 0x784
#define MUSB_HWVERS_2000 0x800
#include "musb_debug.h"
#include "musb_dma.h"
#include "musb_io.h"
#include "musb_regs.h"
#include "musb_gadget.h"
#ifndef __UBOOT__
#include <linux/usb/hcd.h>
#endif
#include "musb_host.h"
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG)
/* NOTE: otg and peripheral-only state machines start at B_IDLE.
* OTG or host-only go to A_IDLE when ID is sensed.
*/
#define is_peripheral_active(m) (!(m)->is_host)
#define is_host_active(m) ((m)->is_host)
#ifdef CONFIG_PROC_FS
#include <linux/fs.h>
#define MUSB_CONFIG_PROC_FS
#endif
/****************************** PERIPHERAL ROLE *****************************/
#ifndef __UBOOT__
#define is_peripheral_capable() (1)
#else
#ifdef CONFIG_USB_MUSB_GADGET
#define is_peripheral_capable() (1)
#else
#define is_peripheral_capable() (0)
#endif
#endif
extern irqreturn_t musb_g_ep0_irq(struct musb *);
extern void musb_g_tx(struct musb *, u8);
extern void musb_g_rx(struct musb *, u8);
extern void musb_g_reset(struct musb *);
extern void musb_g_suspend(struct musb *);
extern void musb_g_resume(struct musb *);
extern void musb_g_wakeup(struct musb *);
extern void musb_g_disconnect(struct musb *);
/****************************** HOST ROLE ***********************************/
#ifndef __UBOOT__
#define is_host_capable() (1)
#else
#ifdef CONFIG_USB_MUSB_HOST
#define is_host_capable() (1)
#else
#define is_host_capable() (0)
#endif
#endif
extern irqreturn_t musb_h_ep0_irq(struct musb *);
extern void musb_host_tx(struct musb *, u8);
extern void musb_host_rx(struct musb *, u8);
/****************************** CONSTANTS ********************************/
#ifndef MUSB_C_NUM_EPS
#define MUSB_C_NUM_EPS ((u8)16)
#endif
#ifndef MUSB_MAX_END0_PACKET
#define MUSB_MAX_END0_PACKET ((u16)MUSB_EP0_FIFOSIZE)
#endif
/* host side ep0 states */
enum musb_h_ep0_state {
MUSB_EP0_IDLE,
MUSB_EP0_START, /* expect ack of setup */
MUSB_EP0_IN, /* expect IN DATA */
MUSB_EP0_OUT, /* expect ack of OUT DATA */
MUSB_EP0_STATUS, /* expect ack of STATUS */
} __attribute__ ((packed));
/* peripheral side ep0 states */
enum musb_g_ep0_state {
MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */
MUSB_EP0_STAGE_SETUP, /* received SETUP */
MUSB_EP0_STAGE_TX, /* IN data */
MUSB_EP0_STAGE_RX, /* OUT data */
MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
MUSB_EP0_STAGE_STATUSOUT, /* (after IN data) */
MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */
} __attribute__ ((packed));
/*
* OTG protocol constants. See USB OTG 1.3 spec,
* sections 5.5 "Device Timings" and 6.6.5 "Timers".
*/
#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */
#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
/*************************** REGISTER ACCESS ********************************/
/* Endpoint registers (other than dynfifo setup) can be accessed either
* directly with the "flat" model, or after setting up an index register.
*/
#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_SOC_OMAP2430) \
|| defined(CONFIG_SOC_OMAP3430) || defined(CONFIG_BLACKFIN) \
|| defined(CONFIG_ARCH_OMAP4)
/* REVISIT indexed access seemed to
* misbehave (on DaVinci) for at least peripheral IN ...
*/
#define MUSB_FLAT_REG
#endif
/* TUSB mapping: "flat" plus ep0 special cases */
#if defined(CONFIG_USB_MUSB_TUSB6010) || \
defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
#define musb_ep_select(_mbase, _epnum) \
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET
/* "flat" mapping: each endpoint has its own i/o address */
#elif defined(MUSB_FLAT_REG)
#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum)))
#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET
/* "indexed" mapping: INDEX register controls register bank select */
#else
#define musb_ep_select(_mbase, _epnum) \
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET
#endif
/****************************** FUNCTIONS ********************************/
#define MUSB_HST_MODE(_musb)\
{ (_musb)->is_host = true; }
#define MUSB_DEV_MODE(_musb) \
{ (_musb)->is_host = false; }
#define test_devctl_hst_mode(_x) \
(musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM)
#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral")
/******************************** TYPES *************************************/
/**
* struct musb_platform_ops - Operations passed to musb_core by HW glue layer
* @init: turns on clocks, sets up platform-specific registers, etc
* @exit: undoes @init
* @set_mode: forcefully changes operating mode
* @try_ilde: tries to idle the IP
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
* @adjust_channel_params: pre check for standard dma channel_program func
*/
struct musb_platform_ops {
int (*init)(struct musb *musb);
int (*exit)(struct musb *musb);
#ifndef __UBOOT__
void (*enable)(struct musb *musb);
#else
int (*enable)(struct musb *musb);
#endif
void (*disable)(struct musb *musb);
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
int (*adjust_channel_params)(struct dma_channel *channel,
u16 packet_sz, u8 *mode,
dma_addr_t *dma_addr, u32 *len);
};
/*
* struct musb_hw_ep - endpoint hardware (bidirectional)
*
* Ordered slightly for better cacheline locality.
*/
struct musb_hw_ep {
struct musb *musb;
void __iomem *fifo;
void __iomem *regs;
#if defined(CONFIG_USB_MUSB_TUSB6010) || \
defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
void __iomem *conf;
#endif
/* index in musb->endpoints[] */
u8 epnum;
/* hardware configuration, possibly dynamic */
bool is_shared_fifo;
bool tx_double_buffered;
bool rx_double_buffered;
u16 max_packet_sz_tx;
u16 max_packet_sz_rx;
struct dma_channel *tx_channel;
struct dma_channel *rx_channel;
#if defined(CONFIG_USB_MUSB_TUSB6010) || \
defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
/* TUSB has "asynchronous" and "synchronous" dma modes */
dma_addr_t fifo_async;
dma_addr_t fifo_sync;
void __iomem *fifo_sync_va;
#endif
void __iomem *target_regs;
/* currently scheduled peripheral endpoint */
struct musb_qh *in_qh;
struct musb_qh *out_qh;
u8 rx_reinit;
u8 tx_reinit;
/* peripheral side */
struct musb_ep ep_in; /* TX */
struct musb_ep ep_out; /* RX */
};
static inline struct musb_request *next_in_request(struct musb_hw_ep *hw_ep)
{
return next_request(&hw_ep->ep_in);
}
static inline struct musb_request *next_out_request(struct musb_hw_ep *hw_ep)
{
return next_request(&hw_ep->ep_out);
}
struct musb_csr_regs {
/* FIFO registers */
u16 txmaxp, txcsr, rxmaxp, rxcsr;
u16 rxfifoadd, txfifoadd;
u8 txtype, txinterval, rxtype, rxinterval;
u8 rxfifosz, txfifosz;
u8 txfunaddr, txhubaddr, txhubport;
u8 rxfunaddr, rxhubaddr, rxhubport;
};
struct musb_context_registers {
u8 power;
u16 intrtxe, intrrxe;
u8 intrusbe;
u16 frame;
u8 index, testmode;
u8 devctl, busctl, misc;
u32 otg_interfsel;
struct musb_csr_regs index_regs[MUSB_C_NUM_EPS];
};
/*
* struct musb - Driver instance data.
*/
struct musb {
/* device lock */
spinlock_t lock;
const struct musb_platform_ops *ops;
struct musb_context_registers context;
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
u16 hwvers;
/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
#define MUSB_PORT_STAT_RESUME (1 << 31)
u32 port1_status;
unsigned long rh_timer;
enum musb_h_ep0_state ep0_stage;
/* bulk traffic normally dedicates endpoint hardware, and each
* direction has its own ring of host side endpoints.
* we try to progress the transfer at the head of each endpoint's
* queue until it completes or NAKs too much; then we try the next
* endpoint.
*/
struct musb_hw_ep *bulk_ep;
struct list_head control; /* of musb_qh */
struct list_head in_bulk; /* of musb_qh */
struct list_head out_bulk; /* of musb_qh */
struct timer_list otg_timer;
struct notifier_block nb;
struct dma_controller *dma_controller;
struct device *controller;
void __iomem *ctrl_base;
void __iomem *mregs;
#if defined(CONFIG_USB_MUSB_TUSB6010) || \
defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
dma_addr_t async;
dma_addr_t sync;
void __iomem *sync_va;
#endif
/* passed down from chip/board specific irq handlers */
u8 int_usb;
u16 int_rx;
u16 int_tx;
struct usb_phy *xceiv;
int nIrq;
unsigned irq_wake:1;
struct musb_hw_ep endpoints[MUSB_C_NUM_EPS];
#define control_ep endpoints
#define VBUSERR_RETRY_COUNT 3
u16 vbuserr_retry;
u16 epmask;
u8 nr_endpoints;
u8 board_mode; /* enum musb_mode */
int (*board_set_power)(int state);
u8 min_power; /* vbus for periph, in mA/2 */
bool is_host;
int a_wait_bcon; /* VBUS timeout in msecs */
unsigned long idle_timeout; /* Next timeout in jiffies */
/* active means connected and not suspended */
unsigned is_active:1;
unsigned is_multipoint:1;
unsigned ignore_disconnect:1; /* during bus resets */
unsigned hb_iso_rx:1; /* high bandwidth iso rx? */
unsigned hb_iso_tx:1; /* high bandwidth iso tx? */
unsigned dyn_fifo:1; /* dynamic FIFO supported? */
unsigned bulk_split:1;
#define can_bulk_split(musb,type) \
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split)
unsigned bulk_combine:1;
#define can_bulk_combine(musb,type) \
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine)
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
/* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1;
/* is_self_powered is reported in device status and the
* config descriptor. is_bus_powered means B_PERIPHERAL
* draws some VBUS current; both can be true.
*/
unsigned is_self_powered:1;
unsigned is_bus_powered:1;
unsigned set_address:1;
unsigned test_mode:1;
unsigned softconnect:1;
u8 address;
u8 test_mode_nr;
u16 ackpend; /* ep0 */
enum musb_g_ep0_state ep0_state;
struct usb_gadget g; /* the gadget */
struct usb_gadget_driver *gadget_driver; /* its driver */
/*
* FIXME: Remove this flag.
*
* This is only added to allow Blackfin to work
* with current driver. For some unknown reason
* Blackfin doesn't work with double buffering
* and that's enabled by default.
*
* We added this flag to forcefully disable double
* buffering until we get it working.
*/
unsigned double_buffer_not_ok:1;
struct musb_hdrc_config *config;
#ifdef MUSB_CONFIG_PROC_FS
struct proc_dir_entry *proc_entry;
#endif
};
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
{
return container_of(g, struct musb, g);
}
#ifdef CONFIG_BLACKFIN
static inline int musb_read_fifosize(struct musb *musb,
struct musb_hw_ep *hw_ep, u8 epnum)
{
musb->nr_endpoints++;
musb->epmask |= (1 << epnum);
if (epnum < 5) {
hw_ep->max_packet_sz_tx = 128;
hw_ep->max_packet_sz_rx = 128;
} else {
hw_ep->max_packet_sz_tx = 1024;
hw_ep->max_packet_sz_rx = 1024;
}
hw_ep->is_shared_fifo = false;
return 0;
}
static inline void musb_configure_ep0(struct musb *musb)
{
musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].is_shared_fifo = true;
}
#else
static inline int musb_read_fifosize(struct musb *musb,
struct musb_hw_ep *hw_ep, u8 epnum)
{
void *mbase = musb->mregs;
u8 reg = 0;
/* read from core using indexed model */
reg = musb_readb(mbase, MUSB_EP_OFFSET(epnum, MUSB_FIFOSIZE));
/* 0's returned when no more endpoints */
if (!reg)
return -ENODEV;
musb->nr_endpoints++;
musb->epmask |= (1 << epnum);
hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f);
/* shared TX/RX FIFO? */
if ((reg & 0xf0) == 0xf0) {
hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx;
hw_ep->is_shared_fifo = true;
return 0;
} else {
hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
hw_ep->is_shared_fifo = false;
}
return 0;
}
static inline void musb_configure_ep0(struct musb *musb)
{
musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].is_shared_fifo = true;
}
#endif /* CONFIG_BLACKFIN */
/***************************** Glue it together *****************************/
extern const char musb_driver_name[];
#ifndef __UBOOT__
extern void musb_start(struct musb *musb);
#else
extern int musb_start(struct musb *musb);
#endif
extern void musb_stop(struct musb *musb);
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
extern void musb_load_testpacket(struct musb *);
extern irqreturn_t musb_interrupt(struct musb *);
extern void musb_hnp_stop(struct musb *musb);
static inline void musb_platform_set_vbus(struct musb *musb, int is_on)
{
if (musb->ops->set_vbus)
musb->ops->set_vbus(musb, is_on);
}
#ifndef __UBOOT__
static inline void musb_platform_enable(struct musb *musb)
{
if (musb->ops->enable)
musb->ops->enable(musb);
}
#else
static inline int musb_platform_enable(struct musb *musb)
{
if (!musb->ops->enable)
return 0;
return musb->ops->enable(musb);
}
#endif
static inline void musb_platform_disable(struct musb *musb)
{
if (musb->ops->disable)
musb->ops->disable(musb);
}
static inline int musb_platform_set_mode(struct musb *musb, u8 mode)
{
if (!musb->ops->set_mode)
return 0;
return musb->ops->set_mode(musb, mode);
}
static inline void musb_platform_try_idle(struct musb *musb,
unsigned long timeout)
{
if (musb->ops->try_idle)
musb->ops->try_idle(musb, timeout);
}
static inline int musb_platform_get_vbus_status(struct musb *musb)
{
if (!musb->ops->vbus_status)
return 0;
return musb->ops->vbus_status(musb);
}
static inline int musb_platform_init(struct musb *musb)
{
if (!musb->ops->init)
return -EINVAL;
return musb->ops->init(musb);
}
static inline int musb_platform_exit(struct musb *musb)
{
if (!musb->ops->exit)
return -EINVAL;
return musb->ops->exit(musb);
}
#ifdef __UBOOT__
struct musb *
musb_init_controller(struct musb_hdrc_platform_data *plat, struct device *dev,
void *ctrl);
#endif
#endif /* __MUSB_CORE_H__ */

View File

@@ -0,0 +1,34 @@
/*
* MUSB OTG driver debug defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_LINUX_DEBUG_H__
#define __MUSB_LINUX_DEBUG_H__
#define yprintk(facility, format, args...) \
do { printk(facility "%s %d: " format , \
__func__, __LINE__ , ## args); } while (0)
#define WARNING(fmt, args...) yprintk(KERN_WARNING, fmt, ## args)
#define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args)
#define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args)
#ifdef CONFIG_DEBUG_FS
int musb_init_debugfs(struct musb *musb);
void musb_exit_debugfs(struct musb *musb);
#else
static inline int musb_init_debugfs(struct musb *musb)
{
return 0;
}
static inline void musb_exit_debugfs(struct musb *musb)
{
}
#endif
#endif /* __MUSB_LINUX_DEBUG_H__ */

View File

@@ -0,0 +1,162 @@
/*
* MUSB OTG driver DMA controller abstraction
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_DMA_H__
#define __MUSB_DMA_H__
struct musb_hw_ep;
/*
* DMA Controller Abstraction
*
* DMA Controllers are abstracted to allow use of a variety of different
* implementations of DMA, as allowed by the Inventra USB cores. On the
* host side, usbcore sets up the DMA mappings and flushes caches; on the
* peripheral side, the gadget controller driver does. Responsibilities
* of a DMA controller driver include:
*
* - Handling the details of moving multiple USB packets
* in cooperation with the Inventra USB core, including especially
* the correct RX side treatment of short packets and buffer-full
* states (both of which terminate transfers).
*
* - Knowing the correlation between dma channels and the
* Inventra core's local endpoint resources and data direction.
*
* - Maintaining a list of allocated/available channels.
*
* - Updating channel status on interrupts,
* whether shared with the Inventra core or separate.
*/
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#ifndef CONFIG_USB_MUSB_PIO_ONLY
#define is_dma_capable() (1)
#else
#define is_dma_capable() (0)
#endif
#ifdef CONFIG_USB_TI_CPPI_DMA
#define is_cppi_enabled() 1
#else
#define is_cppi_enabled() 0
#endif
#ifdef CONFIG_USB_TUSB_OMAP_DMA
#define tusb_dma_omap() 1
#else
#define tusb_dma_omap() 0
#endif
/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1
* Only allow DMA mode 1 to be used when the USB will actually generate the
* interrupts we expect.
*/
#ifdef CONFIG_BLACKFIN
# undef USE_MODE1
# if !ANOMALY_05000456
# define USE_MODE1
# endif
#endif
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
*/
enum dma_channel_status {
/* unallocated */
MUSB_DMA_STATUS_UNKNOWN,
/* allocated ... but not busy, no errors */
MUSB_DMA_STATUS_FREE,
/* busy ... transactions are active */
MUSB_DMA_STATUS_BUSY,
/* transaction(s) aborted due to ... dma or memory bus error */
MUSB_DMA_STATUS_BUS_ABORT,
/* transaction(s) aborted due to ... core error or USB fault */
MUSB_DMA_STATUS_CORE_ABORT
};
struct dma_controller;
/**
* struct dma_channel - A DMA channel.
* @private_data: channel-private data
* @max_len: the maximum number of bytes the channel can move in one
* transaction (typically representing many USB maximum-sized packets)
* @actual_len: how many bytes have been transferred
* @status: current channel status (updated e.g. on interrupt)
* @desired_mode: true if mode 1 is desired; false if mode 0 is desired
*
* channels are associated with an endpoint for the duration of at least
* one usb transfer.
*/
struct dma_channel {
void *private_data;
/* FIXME not void* private_data, but a dma_controller * */
size_t max_len;
size_t actual_len;
enum dma_channel_status status;
bool desired_mode;
};
/*
* dma_channel_status - return status of dma channel
* @c: the channel
*
* Returns the software's view of the channel status. If that status is BUSY
* then it's possible that the hardware has completed (or aborted) a transfer,
* so the driver needs to update that status.
*/
static inline enum dma_channel_status
dma_channel_status(struct dma_channel *c)
{
return (is_dma_capable() && c) ? c->status : MUSB_DMA_STATUS_UNKNOWN;
}
/**
* struct dma_controller - A DMA Controller.
* @start: call this to start a DMA controller;
* return 0 on success, else negative errno
* @stop: call this to stop a DMA controller
* return 0 on success, else negative errno
* @channel_alloc: call this to allocate a DMA channel
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
*
* Controllers manage dma channels.
*/
struct dma_controller {
int (*start)(struct dma_controller *);
int (*stop)(struct dma_controller *);
struct dma_channel *(*channel_alloc)(struct dma_controller *,
struct musb_hw_ep *, u8 is_tx);
void (*channel_release)(struct dma_channel *);
int (*channel_program)(struct dma_channel *channel,
u16 maxpacket, u8 mode,
dma_addr_t dma_addr,
u32 length);
int (*channel_abort)(struct dma_channel *);
int (*is_compatible)(struct dma_channel *channel,
u16 maxpacket,
void *buf, u32 length);
};
/* called after channel_program(), may indicate a fault */
extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit);
extern struct dma_controller *__init
dma_controller_create(struct musb *, void __iomem *);
extern void dma_controller_destroy(struct dma_controller *);
#endif /* __MUSB_DMA_H__ */

View File

@@ -0,0 +1,762 @@
/*
* Texas Instruments DSPS platforms "glue layer"
*
* Copyright (C) 2012, by Texas Instruments
*
* Based on the am35x "glue layer" code.
*
* This file is part of the Inventra Controller Driver for Linux.
*
* SPDX-License-Identifier: GPL-2.0
*
* musb_dsps.c will be a common file for all the TI DSPS platforms
* such as dm64x, dm36x, dm35x, da8x, am35x and ti81x.
* For now only ti81x is using this and in future davinci.c, am35x.c
* da8xx.c would be merged to this file after testing.
*/
#ifndef __UBOOT__
#include <linux/init.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <plat/usb.h>
#else
#include <common.h>
#include <asm/omap_musb.h>
#include "linux-compat.h"
#endif
#include "musb_core.h"
/**
* avoid using musb_readx()/musb_writex() as glue layer should not be
* dependent on musb core layer symbols.
*/
static inline u8 dsps_readb(const void __iomem *addr, unsigned offset)
{ return __raw_readb(addr + offset); }
static inline u32 dsps_readl(const void __iomem *addr, unsigned offset)
{ return __raw_readl(addr + offset); }
static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
{ __raw_writeb(data, addr + offset); }
static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
{ __raw_writel(data, addr + offset); }
/**
* DSPS musb wrapper register offset.
* FIXME: This should be expanded to have all the wrapper registers from TI DSPS
* musb ips.
*/
struct dsps_musb_wrapper {
u16 revision;
u16 control;
u16 status;
u16 eoi;
u16 epintr_set;
u16 epintr_clear;
u16 epintr_status;
u16 coreintr_set;
u16 coreintr_clear;
u16 coreintr_status;
u16 phy_utmi;
u16 mode;
/* bit positions for control */
unsigned reset:5;
/* bit positions for interrupt */
unsigned usb_shift:5;
u32 usb_mask;
u32 usb_bitmap;
unsigned drvvbus:5;
unsigned txep_shift:5;
u32 txep_mask;
u32 txep_bitmap;
unsigned rxep_shift:5;
u32 rxep_mask;
u32 rxep_bitmap;
/* bit positions for phy_utmi */
unsigned otg_disable:5;
/* bit positions for mode */
unsigned iddig:5;
/* miscellaneous stuff */
u32 musb_core_offset;
u8 poll_seconds;
};
static const struct dsps_musb_wrapper ti81xx_driver_data __devinitconst = {
.revision = 0x00,
.control = 0x14,
.status = 0x18,
.eoi = 0x24,
.epintr_set = 0x38,
.epintr_clear = 0x40,
.epintr_status = 0x30,
.coreintr_set = 0x3c,
.coreintr_clear = 0x44,
.coreintr_status = 0x34,
.phy_utmi = 0xe0,
.mode = 0xe8,
.reset = 0,
.otg_disable = 21,
.iddig = 8,
.usb_shift = 0,
.usb_mask = 0x1ff,
.usb_bitmap = (0x1ff << 0),
.drvvbus = 8,
.txep_shift = 0,
.txep_mask = 0xffff,
.txep_bitmap = (0xffff << 0),
.rxep_shift = 16,
.rxep_mask = 0xfffe,
.rxep_bitmap = (0xfffe << 16),
.musb_core_offset = 0x400,
.poll_seconds = 2,
};
/**
* DSPS glue structure.
*/
struct dsps_glue {
struct device *dev;
struct platform_device *musb; /* child musb pdev */
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
struct timer_list timer; /* otg_workaround timer */
};
/**
* dsps_musb_enable - enable interrupts
*/
#ifndef __UBOOT__
static void dsps_musb_enable(struct musb *musb)
#else
static int dsps_musb_enable(struct musb *musb)
#endif
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
#else
const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data;
#endif
void __iomem *reg_base = musb->ctrl_base;
u32 epmask, coremask;
/* Workaround: setup IRQs through both register sets. */
epmask = ((musb->epmask & wrp->txep_mask) << wrp->txep_shift) |
((musb->epmask & wrp->rxep_mask) << wrp->rxep_shift);
coremask = (wrp->usb_bitmap & ~MUSB_INTR_SOF);
dsps_writel(reg_base, wrp->epintr_set, epmask);
dsps_writel(reg_base, wrp->coreintr_set, coremask);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
#ifndef __UBOOT__
if (is_otg_enabled(musb))
dsps_writel(reg_base, wrp->coreintr_set,
(1 << wrp->drvvbus) << wrp->usb_shift);
#else
return 0;
#endif
}
/**
* dsps_musb_disable - disable HDRC and flush interrupts
*/
static void dsps_musb_disable(struct musb *musb)
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
void __iomem *reg_base = musb->ctrl_base;
dsps_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
dsps_writel(reg_base, wrp->epintr_clear,
wrp->txep_bitmap | wrp->rxep_bitmap);
dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
dsps_writel(reg_base, wrp->eoi, 0);
#endif
}
#ifndef __UBOOT__
static void otg_timer(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
void __iomem *mregs = musb->mregs;
struct device *dev = musb->controller;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
u8 devctl;
unsigned long flags;
/*
* We poll because DSPS IP's won't expose several OTG-critical
* status change events (from the transceiver) otherwise.
*/
devctl = dsps_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
otg_state_string(musb->xceiv->state));
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
case OTG_STATE_A_WAIT_BCON:
devctl &= ~MUSB_DEVCTL_SESSION;
dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl);
devctl = dsps_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
musb->xceiv->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
case OTG_STATE_A_WAIT_VFALL:
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
dsps_writel(musb->ctrl_base, wrp->coreintr_set,
MUSB_INTR_VBUSERROR << wrp->usb_shift);
break;
case OTG_STATE_B_IDLE:
if (!is_peripheral_enabled(musb))
break;
devctl = dsps_readb(mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&glue->timer,
jiffies + wrp->poll_seconds * HZ);
else
musb->xceiv->state = OTG_STATE_A_IDLE;
break;
default:
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
{
struct device *dev = musb->controller;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
static unsigned long last_timer;
if (!is_otg_enabled(musb))
return;
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || (musb->a_wait_bcon == 0 &&
musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
otg_state_string(musb->xceiv->state));
del_timer(&glue->timer);
last_timer = jiffies;
return;
}
if (time_after(last_timer, timeout) && timer_pending(&glue->timer)) {
dev_dbg(musb->controller,
"Longer idle timer already pending, ignoring...\n");
return;
}
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
otg_state_string(musb->xceiv->state),
jiffies_to_msecs(timeout - jiffies));
mod_timer(&glue->timer, timeout);
}
#endif
static irqreturn_t dsps_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
void __iomem *reg_base = musb->ctrl_base;
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
#else
const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data;
#endif
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
u32 epintr, usbintr;
spin_lock_irqsave(&musb->lock, flags);
/* Get endpoint interrupts */
epintr = dsps_readl(reg_base, wrp->epintr_status);
musb->int_rx = (epintr & wrp->rxep_bitmap) >> wrp->rxep_shift;
musb->int_tx = (epintr & wrp->txep_bitmap) >> wrp->txep_shift;
if (epintr)
dsps_writel(reg_base, wrp->epintr_status, epintr);
/* Get usb core interrupts */
usbintr = dsps_readl(reg_base, wrp->coreintr_status);
if (!usbintr && !epintr)
goto eoi;
musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift;
if (usbintr)
dsps_writel(reg_base, wrp->coreintr_status, usbintr);
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
usbintr, epintr);
#ifndef __UBOOT__
/*
* DRVVBUS IRQs are the only proxy we have (a very poor one!) for
* DSPS IP's missing ID change IRQ. We need an ID change IRQ to
* switch appropriately between halves of the OTG state machine.
* Managing DEVCTL.SESSION per Mentor docs requires that we know its
* value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
*/
if ((usbintr & MUSB_INTR_BABBLE) && is_host_enabled(musb))
pr_info("CAUTION: musb: Babble Interrupt Occured\n");
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
int drvvbus = dsps_readl(reg_base, wrp->status);
void __iomem *mregs = musb->mregs;
u8 devctl = dsps_readb(mregs, MUSB_DEVCTL);
int err;
err = is_host_enabled(musb) && (musb->int_usb &
MUSB_INTR_VBUSERROR);
if (err) {
/*
* The Mentor core doesn't debounce VBUS as needed
* to cope with device connect current spikes. This
* means it's not uncommon for bus-powered devices
* to get VBUS errors during enumeration.
*
* This is a workaround, but newer RTL from Mentor
* seems to allow a better one: "re"-starting sessions
* without waiting for VBUS to stop registering in
* devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&glue->timer,
jiffies + wrp->poll_seconds * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (is_host_enabled(musb) && drvvbus) {
musb->is_active = 1;
MUSB_HST_MODE(musb);
musb->xceiv->otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
del_timer(&glue->timer);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
musb->xceiv->otg->default_a = 0;
musb->xceiv->state = OTG_STATE_B_IDLE;
}
/* NOTE: this must complete power-on within 100 ms. */
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
otg_state_string(musb->xceiv->state),
err ? " ERROR" : "",
devctl);
ret = IRQ_HANDLED;
}
#endif
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret |= musb_interrupt(musb);
eoi:
/* EOI needs to be written for the IRQ to be re-asserted. */
if (ret == IRQ_HANDLED || epintr || usbintr)
dsps_writel(reg_base, wrp->eoi, 1);
#ifndef __UBOOT__
/* Poll for ID change */
if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
#endif
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
static int dsps_musb_init(struct musb *musb)
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
struct omap_musb_board_data *data = plat->board_data;
#else
struct omap_musb_board_data *data =
(struct omap_musb_board_data *)musb->controller;
const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data;
#endif
void __iomem *reg_base = musb->ctrl_base;
u32 rev, val;
int status;
/* mentor core register starts at offset of 0x400 from musb base */
musb->mregs += wrp->musb_core_offset;
#ifndef __UBOOT__
/* NOP driver needs change if supporting dual instance */
usb_nop_xceiv_register();
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(musb->xceiv))
return -ENODEV;
#endif
/* Returns zero if e.g. not clocked */
rev = dsps_readl(reg_base, wrp->revision);
if (!rev) {
status = -ENODEV;
goto err0;
}
#ifndef __UBOOT__
if (is_host_enabled(musb))
setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
#endif
/* Reset the musb */
dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
/* Start the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(1);
musb->isr = dsps_interrupt;
/* reset the otgdisable bit, needed for host mode to work */
val = dsps_readl(reg_base, wrp->phy_utmi);
val &= ~(1 << wrp->otg_disable);
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
/* clear level interrupt */
dsps_writel(reg_base, wrp->eoi, 0);
return 0;
err0:
#ifndef __UBOOT__
usb_put_phy(musb->xceiv);
usb_nop_xceiv_unregister();
#endif
return status;
}
static int dsps_musb_exit(struct musb *musb)
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
#else
struct omap_musb_board_data *data =
(struct omap_musb_board_data *)musb->controller;
#endif
#ifndef __UBOOT__
if (is_host_enabled(musb))
del_timer_sync(&glue->timer);
#endif
/* Shutdown the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(0);
#ifndef __UBOOT__
/* NOP driver needs change if supporting dual instance */
usb_put_phy(musb->xceiv);
usb_nop_xceiv_unregister();
#endif
return 0;
}
#ifndef __UBOOT__
static struct musb_platform_ops dsps_ops = {
#else
struct musb_platform_ops musb_dsps_ops = {
#endif
.init = dsps_musb_init,
.exit = dsps_musb_exit,
.enable = dsps_musb_enable,
.disable = dsps_musb_disable,
#ifndef __UBOOT__
.try_idle = dsps_musb_try_idle,
#endif
};
#ifndef __UBOOT__
static u64 musb_dmamask = DMA_BIT_MASK(32);
#endif
#ifndef __UBOOT__
static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
{
struct device *dev = glue->dev;
struct platform_device *pdev = to_platform_device(dev);
struct musb_hdrc_platform_data *pdata = dev->platform_data;
struct platform_device *musb;
struct resource *res;
struct resource resources[2];
char res_name[10];
int ret;
/* get memory resource */
sprintf(res_name, "musb%d", id);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
if (!res) {
dev_err(dev, "%s get mem resource failed\n", res_name);
ret = -ENODEV;
goto err0;
}
res->parent = NULL;
resources[0] = *res;
/* get irq resource */
sprintf(res_name, "musb%d-irq", id);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
if (!res) {
dev_err(dev, "%s get irq resource failed\n", res_name);
ret = -ENODEV;
goto err0;
}
res->parent = NULL;
resources[1] = *res;
resources[1].name = "mc";
/* allocate the child platform device */
musb = platform_device_alloc("musb-hdrc", -1);
if (!musb) {
dev_err(dev, "failed to allocate musb device\n");
ret = -ENOMEM;
goto err0;
}
musb->dev.parent = dev;
musb->dev.dma_mask = &musb_dmamask;
musb->dev.coherent_dma_mask = musb_dmamask;
glue->musb = musb;
pdata->platform_ops = &dsps_ops;
ret = platform_device_add_resources(musb, resources, 2);
if (ret) {
dev_err(dev, "failed to add resources\n");
goto err1;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(dev, "failed to add platform_data\n");
goto err1;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(dev, "failed to register musb device\n");
goto err1;
}
return 0;
err1:
platform_device_put(musb);
err0:
return ret;
}
static void __devexit dsps_delete_musb_pdev(struct dsps_glue *glue)
{
platform_device_del(glue->musb);
platform_device_put(glue->musb);
}
static int __devinit dsps_probe(struct platform_device *pdev)
{
const struct platform_device_id *id = platform_get_device_id(pdev);
const struct dsps_musb_wrapper *wrp =
(struct dsps_musb_wrapper *)id->driver_data;
struct dsps_glue *glue;
struct resource *iomem;
int ret;
/* allocate glue */
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "unable to allocate glue memory\n");
ret = -ENOMEM;
goto err0;
}
/* get memory resource */
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
dev_err(&pdev->dev, "failed to get usbss mem resourse\n");
ret = -ENODEV;
goto err1;
}
glue->dev = &pdev->dev;
glue->wrp = kmemdup(wrp, sizeof(*wrp), GFP_KERNEL);
if (!glue->wrp) {
dev_err(&pdev->dev, "failed to duplicate wrapper struct memory\n");
ret = -ENOMEM;
goto err1;
}
platform_set_drvdata(pdev, glue);
/* enable the usbss clocks */
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "pm_runtime_get_sync FAILED");
goto err2;
}
/* create the child platform device for first instances of musb */
ret = dsps_create_musb_pdev(glue, 0);
if (ret != 0) {
dev_err(&pdev->dev, "failed to create child pdev\n");
goto err3;
}
return 0;
err3:
pm_runtime_put(&pdev->dev);
err2:
pm_runtime_disable(&pdev->dev);
kfree(glue->wrp);
err1:
kfree(glue);
err0:
return ret;
}
static int __devexit dsps_remove(struct platform_device *pdev)
{
struct dsps_glue *glue = platform_get_drvdata(pdev);
/* delete the child platform device */
dsps_delete_musb_pdev(glue);
/* disable usbss clocks */
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
kfree(glue->wrp);
kfree(glue);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int dsps_suspend(struct device *dev)
{
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
/* Shutdown the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(0);
return 0;
}
static int dsps_resume(struct device *dev)
{
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
/* Start the on-chip PHY and its PLL. */
if (data->set_phy_power)
data->set_phy_power(1);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume);
#endif
#ifndef __UBOOT__
static const struct platform_device_id musb_dsps_id_table[] __devinitconst = {
{
.name = "musb-ti81xx",
.driver_data = (kernel_ulong_t) &ti81xx_driver_data,
},
{ }, /* Terminating Entry */
};
MODULE_DEVICE_TABLE(platform, musb_dsps_id_table);
static const struct of_device_id musb_dsps_of_match[] __devinitconst = {
{ .compatible = "musb-ti81xx", },
{ .compatible = "ti,ti81xx-musb", },
{ .compatible = "ti,am335x-musb", },
{ },
};
MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
static struct platform_driver dsps_usbss_driver = {
.probe = dsps_probe,
.remove = __devexit_p(dsps_remove),
.driver = {
.name = "musb-dsps",
.pm = &dsps_pm_ops,
.of_match_table = musb_dsps_of_match,
},
.id_table = musb_dsps_id_table,
};
MODULE_DESCRIPTION("TI DSPS MUSB Glue Layer");
MODULE_AUTHOR("Ravi B <ravibabu@ti.com>");
MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>");
MODULE_LICENSE("GPL v2");
static int __init dsps_init(void)
{
return platform_driver_register(&dsps_usbss_driver);
}
subsys_initcall(dsps_init);
static void __exit dsps_exit(void)
{
platform_driver_unregister(&dsps_usbss_driver);
}
module_exit(dsps_exit);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,106 @@
/*
* MUSB OTG driver peripheral defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_GADGET_H
#define __MUSB_GADGET_H
#include <linux/list.h>
#ifdef __UBOOT__
#include <asm/byteorder.h>
#include <asm/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#endif
enum buffer_map_state {
UN_MAPPED = 0,
PRE_MAPPED,
MUSB_MAPPED
};
struct musb_request {
struct usb_request request;
struct list_head list;
struct musb_ep *ep;
struct musb *musb;
u8 tx; /* endpoint direction */
u8 epnum;
enum buffer_map_state map_state;
};
static inline struct musb_request *to_musb_request(struct usb_request *req)
{
return req ? container_of(req, struct musb_request, request) : NULL;
}
extern struct usb_request *
musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
extern void musb_free_request(struct usb_ep *ep, struct usb_request *req);
/*
* struct musb_ep - peripheral side view of endpoint rx or tx side
*/
struct musb_ep {
/* stuff towards the head is basically write-once. */
struct usb_ep end_point;
char name[12];
struct musb_hw_ep *hw_ep;
struct musb *musb;
u8 current_epnum;
/* ... when enabled/disabled ... */
u8 type;
u8 is_in;
u16 packet_sz;
const struct usb_endpoint_descriptor *desc;
struct dma_channel *dma;
/* later things are modified based on usage */
struct list_head req_list;
u8 wedged;
/* true if lock must be dropped but req_list may not be advanced */
u8 busy;
u8 hb_mult;
};
static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
{
return ep ? container_of(ep, struct musb_ep, end_point) : NULL;
}
static inline struct musb_request *next_request(struct musb_ep *ep)
{
struct list_head *queue = &ep->req_list;
if (list_empty(queue))
return NULL;
return container_of(queue->next, struct musb_request, list);
}
extern void musb_g_tx(struct musb *musb, u8 epnum);
extern void musb_g_rx(struct musb *musb, u8 epnum);
extern const struct usb_ep_ops musb_g_ep0_ops;
extern int musb_gadget_setup(struct musb *);
extern void musb_gadget_cleanup(struct musb *);
extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int);
extern void musb_ep_restart(struct musb *, struct musb_request *);
#ifdef __UBOOT__
int musb_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver);
#endif
#endif /* __MUSB_GADGET_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
/*
* MUSB OTG driver host defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef _MUSB_HOST_H
#define _MUSB_HOST_H
#ifdef __UBOOT__
#include "usb-compat.h"
#endif
static inline struct usb_hcd *musb_to_hcd(struct musb *musb)
{
return container_of((void *) musb, struct usb_hcd, hcd_priv);
}
static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
{
return (struct musb *) (hcd->hcd_priv);
}
/* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */
struct musb_qh {
struct usb_host_endpoint *hep; /* usbcore info */
struct usb_device *dev;
struct musb_hw_ep *hw_ep; /* current binding */
struct list_head ring; /* of musb_qh */
/* struct musb_qh *next; */ /* for periodic tree */
u8 mux; /* qh multiplexed to hw_ep */
unsigned offset; /* in urb->transfer_buffer */
unsigned segsize; /* current xfer fragment */
u8 type_reg; /* {rx,tx} type register */
u8 intv_reg; /* {rx,tx} interval register */
u8 addr_reg; /* device address register */
u8 h_addr_reg; /* hub address register */
u8 h_port_reg; /* hub port register */
u8 is_ready; /* safe to modify hw_ep */
u8 type; /* XFERTYPE_* */
u8 epnum;
u8 hb_mult; /* high bandwidth pkts per uf */
u16 maxpacket;
u16 frame; /* for periodic schedule */
unsigned iso_idx; /* in urb->iso_frame_desc[] */
};
/* map from control or bulk queue head to the first qh on that ring */
static inline struct musb_qh *first_qh(struct list_head *q)
{
if (list_empty(q))
return NULL;
return list_entry(q->next, struct musb_qh, ring);
}
extern void musb_root_disconnect(struct musb *musb);
struct usb_hcd;
extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf);
extern int musb_hub_control(struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
extern const struct hc_driver musb_hc_driver;
static inline struct urb *next_urb(struct musb_qh *qh)
{
struct list_head *queue;
if (!qh)
return NULL;
queue = &qh->hep->urb_list;
if (list_empty(queue))
return NULL;
return list_entry(queue->next, struct urb, urb_list);
}
#ifdef __UBOOT__
int musb_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
#endif
#endif /* _MUSB_HOST_H */

View File

@@ -0,0 +1,126 @@
/*
* MUSB OTG driver register I/O
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_LINUX_PLATFORM_ARCH_H__
#define __MUSB_LINUX_PLATFORM_ARCH_H__
#ifndef __UBOOT__
#include <linux/io.h>
#else
#include <asm/io.h>
#endif
#if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \
&& !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \
&& !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) \
&& !defined(CONFIG_MIPS) && !defined(CONFIG_M68K)
static inline void readsl(const void __iomem *addr, void *buf, int len)
{ insl((unsigned long)addr, buf, len); }
static inline void readsw(const void __iomem *addr, void *buf, int len)
{ insw((unsigned long)addr, buf, len); }
static inline void readsb(const void __iomem *addr, void *buf, int len)
{ insb((unsigned long)addr, buf, len); }
static inline void writesl(const void __iomem *addr, const void *buf, int len)
{ outsl((unsigned long)addr, buf, len); }
static inline void writesw(const void __iomem *addr, const void *buf, int len)
{ outsw((unsigned long)addr, buf, len); }
static inline void writesb(const void __iomem *addr, const void *buf, int len)
{ outsb((unsigned long)addr, buf, len); }
#endif
#ifndef CONFIG_BLACKFIN
/* NOTE: these offsets are all in bytes */
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
{ return __raw_readw(addr + offset); }
static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
{ return __raw_readl(addr + offset); }
static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
{ __raw_writew(data, addr + offset); }
static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
{ __raw_writel(data, addr + offset); }
#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE)
/*
* TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
*/
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
{
u16 tmp;
u8 val;
tmp = __raw_readw(addr + (offset & ~1));
if (offset & 1)
val = (tmp >> 8);
else
val = tmp & 0xff;
return val;
}
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
{
u16 tmp;
tmp = __raw_readw(addr + (offset & ~1));
if (offset & 1)
tmp = (data << 8) | (tmp & 0xff);
else
tmp = (tmp & 0xff00) | data;
__raw_writew(tmp, addr + (offset & ~1));
}
#else
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
{ return __raw_readb(addr + offset); }
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
{ __raw_writeb(data, addr + offset); }
#endif /* CONFIG_USB_MUSB_TUSB6010 */
#else
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
{ return (u8) (bfin_read16(addr + offset)); }
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
{ return bfin_read16(addr + offset); }
static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
{ return (u32) (bfin_read16(addr + offset)); }
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
{ bfin_write16(addr + offset, (u16) data); }
static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
{ bfin_write16(addr + offset, data); }
static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
{ bfin_write16(addr + offset, (u16) data); }
#endif /* CONFIG_BLACKFIN */
#endif

View File

@@ -0,0 +1,718 @@
/*
* MUSB OTG driver register defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_REGS_H__
#define __MUSB_REGS_H__
#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */
/*
* MUSB Register bits
*/
/* POWER */
#define MUSB_POWER_ISOUPDATE 0x80
#define MUSB_POWER_SOFTCONN 0x40
#define MUSB_POWER_HSENAB 0x20
#define MUSB_POWER_HSMODE 0x10
#define MUSB_POWER_RESET 0x08
#define MUSB_POWER_RESUME 0x04
#define MUSB_POWER_SUSPENDM 0x02
#define MUSB_POWER_ENSUSPEND 0x01
/* INTRUSB */
#define MUSB_INTR_SUSPEND 0x01
#define MUSB_INTR_RESUME 0x02
#define MUSB_INTR_RESET 0x04
#define MUSB_INTR_BABBLE 0x04
#define MUSB_INTR_SOF 0x08
#define MUSB_INTR_CONNECT 0x10
#define MUSB_INTR_DISCONNECT 0x20
#define MUSB_INTR_SESSREQ 0x40
#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */
/* DEVCTL */
#define MUSB_DEVCTL_BDEVICE 0x80
#define MUSB_DEVCTL_FSDEV 0x40
#define MUSB_DEVCTL_LSDEV 0x20
#define MUSB_DEVCTL_VBUS 0x18
#define MUSB_DEVCTL_VBUS_SHIFT 3
#define MUSB_DEVCTL_HM 0x04
#define MUSB_DEVCTL_HR 0x02
#define MUSB_DEVCTL_SESSION 0x01
/* MUSB ULPI VBUSCONTROL */
#define MUSB_ULPI_USE_EXTVBUS 0x01
#define MUSB_ULPI_USE_EXTVBUSIND 0x02
/* ULPI_REG_CONTROL */
#define MUSB_ULPI_REG_REQ (1 << 0)
#define MUSB_ULPI_REG_CMPLT (1 << 1)
#define MUSB_ULPI_RDN_WR (1 << 2)
/* TESTMODE */
#define MUSB_TEST_FORCE_HOST 0x80
#define MUSB_TEST_FIFO_ACCESS 0x40
#define MUSB_TEST_FORCE_FS 0x20
#define MUSB_TEST_FORCE_HS 0x10
#define MUSB_TEST_PACKET 0x08
#define MUSB_TEST_K 0x04
#define MUSB_TEST_J 0x02
#define MUSB_TEST_SE0_NAK 0x01
/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */
#define MUSB_FIFOSZ_DPB 0x10
/* Allocation size (8, 16, 32, ... 4096) */
#define MUSB_FIFOSZ_SIZE 0x0f
/* CSR0 */
#define MUSB_CSR0_FLUSHFIFO 0x0100
#define MUSB_CSR0_TXPKTRDY 0x0002
#define MUSB_CSR0_RXPKTRDY 0x0001
/* CSR0 in Peripheral mode */
#define MUSB_CSR0_P_SVDSETUPEND 0x0080
#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040
#define MUSB_CSR0_P_SENDSTALL 0x0020
#define MUSB_CSR0_P_SETUPEND 0x0010
#define MUSB_CSR0_P_DATAEND 0x0008
#define MUSB_CSR0_P_SENTSTALL 0x0004
/* CSR0 in Host mode */
#define MUSB_CSR0_H_DIS_PING 0x0800
#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */
#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */
#define MUSB_CSR0_H_NAKTIMEOUT 0x0080
#define MUSB_CSR0_H_STATUSPKT 0x0040
#define MUSB_CSR0_H_REQPKT 0x0020
#define MUSB_CSR0_H_ERROR 0x0010
#define MUSB_CSR0_H_SETUPPKT 0x0008
#define MUSB_CSR0_H_RXSTALL 0x0004
/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_CSR0_P_WZC_BITS \
(MUSB_CSR0_P_SENTSTALL)
#define MUSB_CSR0_H_WZC_BITS \
(MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \
| MUSB_CSR0_RXPKTRDY)
/* TxType/RxType */
#define MUSB_TYPE_SPEED 0xc0
#define MUSB_TYPE_SPEED_SHIFT 6
#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */
#define MUSB_TYPE_PROTO_SHIFT 4
#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */
/* CONFIGDATA */
#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */
#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */
#define MUSB_CONFIGDATA_BIGENDIAN 0x20
#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */
#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */
#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */
#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */
#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */
/* TXCSR in Peripheral and Host mode */
#define MUSB_TXCSR_AUTOSET 0x8000
#define MUSB_TXCSR_DMAENAB 0x1000
#define MUSB_TXCSR_FRCDATATOG 0x0800
#define MUSB_TXCSR_DMAMODE 0x0400
#define MUSB_TXCSR_CLRDATATOG 0x0040
#define MUSB_TXCSR_FLUSHFIFO 0x0008
#define MUSB_TXCSR_FIFONOTEMPTY 0x0002
#define MUSB_TXCSR_TXPKTRDY 0x0001
/* TXCSR in Peripheral mode */
#define MUSB_TXCSR_P_ISO 0x4000
#define MUSB_TXCSR_P_INCOMPTX 0x0080
#define MUSB_TXCSR_P_SENTSTALL 0x0020
#define MUSB_TXCSR_P_SENDSTALL 0x0010
#define MUSB_TXCSR_P_UNDERRUN 0x0004
/* TXCSR in Host mode */
#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200
#define MUSB_TXCSR_H_DATATOGGLE 0x0100
#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080
#define MUSB_TXCSR_H_RXSTALL 0x0020
#define MUSB_TXCSR_H_ERROR 0x0004
/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_TXCSR_P_WZC_BITS \
(MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \
| MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY)
#define MUSB_TXCSR_H_WZC_BITS \
(MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \
| MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY)
/* RXCSR in Peripheral and Host mode */
#define MUSB_RXCSR_AUTOCLEAR 0x8000
#define MUSB_RXCSR_DMAENAB 0x2000
#define MUSB_RXCSR_DISNYET 0x1000
#define MUSB_RXCSR_PID_ERR 0x1000
#define MUSB_RXCSR_DMAMODE 0x0800
#define MUSB_RXCSR_INCOMPRX 0x0100
#define MUSB_RXCSR_CLRDATATOG 0x0080
#define MUSB_RXCSR_FLUSHFIFO 0x0010
#define MUSB_RXCSR_DATAERROR 0x0008
#define MUSB_RXCSR_FIFOFULL 0x0002
#define MUSB_RXCSR_RXPKTRDY 0x0001
/* RXCSR in Peripheral mode */
#define MUSB_RXCSR_P_ISO 0x4000
#define MUSB_RXCSR_P_SENTSTALL 0x0040
#define MUSB_RXCSR_P_SENDSTALL 0x0020
#define MUSB_RXCSR_P_OVERRUN 0x0004
/* RXCSR in Host mode */
#define MUSB_RXCSR_H_AUTOREQ 0x4000
#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400
#define MUSB_RXCSR_H_DATATOGGLE 0x0200
#define MUSB_RXCSR_H_RXSTALL 0x0040
#define MUSB_RXCSR_H_REQPKT 0x0020
#define MUSB_RXCSR_H_ERROR 0x0004
/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_RXCSR_P_WZC_BITS \
(MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \
| MUSB_RXCSR_RXPKTRDY)
#define MUSB_RXCSR_H_WZC_BITS \
(MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \
| MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY)
/* HUBADDR */
#define MUSB_HUBADDR_MULTI_TT 0x80
#ifndef CONFIG_BLACKFIN
/* SUNXI has different reg addresses, but identical r/w functions */
#ifndef CONFIG_ARCH_SUNXI
/*
* Common USB registers
*/
#define MUSB_FADDR 0x00 /* 8-bit */
#define MUSB_POWER 0x01 /* 8-bit */
#define MUSB_INTRTX 0x02 /* 16-bit */
#define MUSB_INTRRX 0x04
#define MUSB_INTRTXE 0x06
#define MUSB_INTRRXE 0x08
#define MUSB_INTRUSB 0x0A /* 8 bit */
#define MUSB_INTRUSBE 0x0B /* 8 bit */
#define MUSB_FRAME 0x0C
#define MUSB_INDEX 0x0E /* 8 bit */
#define MUSB_TESTMODE 0x0F /* 8 bit */
/* Get offset for a given FIFO from musb->mregs */
#if defined(CONFIG_USB_MUSB_TUSB6010) || \
defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
#else
#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
#endif
/*
* Additional Control Registers
*/
#define MUSB_DEVCTL 0x60 /* 8 bit */
/* These are always controlled through the INDEX register */
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */
#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */
#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */
/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */
#define MUSB_HWVERS 0x6C /* 8 bit */
#define MUSB_ULPI_BUSCONTROL 0x70 /* 8 bit */
#define MUSB_ULPI_INT_MASK 0x72 /* 8 bit */
#define MUSB_ULPI_INT_SRC 0x73 /* 8 bit */
#define MUSB_ULPI_REG_DATA 0x74 /* 8 bit */
#define MUSB_ULPI_REG_ADDR 0x75 /* 8 bit */
#define MUSB_ULPI_REG_CONTROL 0x76 /* 8 bit */
#define MUSB_ULPI_RAW_DATA 0x77 /* 8 bit */
#define MUSB_EPINFO 0x78 /* 8 bit */
#define MUSB_RAMINFO 0x79 /* 8 bit */
#define MUSB_LINKINFO 0x7a /* 8 bit */
#define MUSB_VPLEN 0x7b /* 8 bit */
#define MUSB_HS_EOF1 0x7c /* 8 bit */
#define MUSB_FS_EOF1 0x7d /* 8 bit */
#define MUSB_LS_EOF1 0x7e /* 8 bit */
/* Offsets to endpoint registers */
#define MUSB_TXMAXP 0x00
#define MUSB_TXCSR 0x02
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
#define MUSB_RXMAXP 0x04
#define MUSB_RXCSR 0x06
#define MUSB_RXCOUNT 0x08
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
#define MUSB_TXTYPE 0x0A
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
#define MUSB_TXINTERVAL 0x0B
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
#define MUSB_RXTYPE 0x0C
#define MUSB_RXINTERVAL 0x0D
#define MUSB_FIFOSIZE 0x0F
#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */
/* Offsets to endpoint registers in indexed model (using INDEX register) */
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
(0x10 + (_offset))
/* Offsets to endpoint registers in flat models */
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
(0x100 + (0x10*(_epnum)) + (_offset))
#if defined(CONFIG_USB_MUSB_TUSB6010) || \
defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
/* TUSB6010 EP0 configuration register is special */
#define MUSB_TUSB_OFFSET(_epnum, _offset) \
(0x10 + _offset)
#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */
#endif
#define MUSB_TXCSR_MODE 0x2000
/* "bus control"/target registers, for host side multipoint (external hubs) */
#define MUSB_TXFUNCADDR 0x00
#define MUSB_TXHUBADDR 0x02
#define MUSB_TXHUBPORT 0x03
#define MUSB_RXFUNCADDR 0x04
#define MUSB_RXHUBADDR 0x06
#define MUSB_RXHUBPORT 0x07
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \
(0x80 + (8*(_epnum)) + (_offset))
#else /* CONFIG_ARCH_SUNXI */
/*
* Common USB registers
*/
#define MUSB_FADDR 0x0098
#define MUSB_POWER 0x0040
#define MUSB_INTRTX 0x0044
#define MUSB_INTRRX 0x0046
#define MUSB_INTRTXE 0x0048
#define MUSB_INTRRXE 0x004A
#define MUSB_INTRUSB 0x004C
#define MUSB_INTRUSBE 0x0050
#define MUSB_FRAME 0x0054
#define MUSB_INDEX 0x0042
#define MUSB_TESTMODE 0x007C
/* Get offset for a given FIFO from musb->mregs */
#define MUSB_FIFO_OFFSET(epnum) (0x00 + ((epnum) * 4))
/*
* Additional Control Registers
*/
#define MUSB_DEVCTL 0x0041
/* These are always controlled through the INDEX register */
#define MUSB_TXFIFOSZ 0x0090
#define MUSB_RXFIFOSZ 0x0094
#define MUSB_TXFIFOADD 0x0092
#define MUSB_RXFIFOADD 0x0096
#define MUSB_EPINFO 0x0078
#define MUSB_RAMINFO 0x0079
#define MUSB_LINKINFO 0x007A
#define MUSB_VPLEN 0x007B
#define MUSB_HS_EOF1 0x007C
#define MUSB_FS_EOF1 0x007D
#define MUSB_LS_EOF1 0x007E
/* Offsets to endpoint registers */
#define MUSB_TXMAXP 0x0080
#define MUSB_TXCSR 0x0082
#define MUSB_CSR0 0x0082
#define MUSB_RXMAXP 0x0084
#define MUSB_RXCSR 0x0086
#define MUSB_RXCOUNT 0x0088
#define MUSB_COUNT0 0x0088
#define MUSB_TXTYPE 0x008C
#define MUSB_TYPE0 0x008C
#define MUSB_TXINTERVAL 0x008D
#define MUSB_NAKLIMIT0 0x008D
#define MUSB_RXTYPE 0x008E
#define MUSB_RXINTERVAL 0x008F
#define MUSB_CONFIGDATA 0x00b0 /* musb_read_configdata adds 0x10 ! */
#define MUSB_FIFOSIZE 0x0090
/* Offsets to endpoint registers in indexed model (using INDEX register) */
#define MUSB_INDEXED_OFFSET(_epnum, _offset) (_offset)
#define MUSB_TXCSR_MODE 0x2000
/* "bus control"/target registers, for host side multipoint (external hubs) */
#define MUSB_TXFUNCADDR 0x0098
#define MUSB_TXHUBADDR 0x009A
#define MUSB_TXHUBPORT 0x009B
#define MUSB_RXFUNCADDR 0x009C
#define MUSB_RXHUBADDR 0x009E
#define MUSB_RXHUBPORT 0x009F
/* Endpoint is selected with MUSB_INDEX. */
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) (_offset)
#endif /* CONFIG_ARCH_SUNXI */
static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
{
musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
}
static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
{
musb_writew(mbase, MUSB_TXFIFOADD, c_off);
}
static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
{
musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
}
static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
{
musb_writew(mbase, MUSB_RXFIFOADD, c_off);
}
static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val)
{
#ifndef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */
musb_writeb(mbase, MUSB_ULPI_BUSCONTROL, val);
#endif
}
static inline u8 musb_read_txfifosz(void __iomem *mbase)
{
return musb_readb(mbase, MUSB_TXFIFOSZ);
}
static inline u16 musb_read_txfifoadd(void __iomem *mbase)
{
return musb_readw(mbase, MUSB_TXFIFOADD);
}
static inline u8 musb_read_rxfifosz(void __iomem *mbase)
{
return musb_readb(mbase, MUSB_RXFIFOSZ);
}
static inline u16 musb_read_rxfifoadd(void __iomem *mbase)
{
return musb_readw(mbase, MUSB_RXFIFOADD);
}
static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase)
{
#ifdef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */
return 0;
#else
return musb_readb(mbase, MUSB_ULPI_BUSCONTROL);
#endif
}
static inline u8 musb_read_configdata(void __iomem *mbase)
{
#if defined CONFIG_MACH_SUN8I_A33 || defined CONFIG_MACH_SUN8I_A83T
/* <Sigh> allwinner saves a reg, and we need to hardcode this */
return 0xde;
#else
musb_writeb(mbase, MUSB_INDEX, 0);
return musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
#endif
}
static inline u16 musb_read_hwvers(void __iomem *mbase)
{
#ifdef CONFIG_ARCH_SUNXI
return 0; /* Unknown version */
#else
return musb_readw(mbase, MUSB_HWVERS);
#endif
}
static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
{
return (MUSB_BUSCTL_OFFSET(i, 0) + mbase);
}
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
u8 qh_addr_reg)
{
musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg);
}
static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
u8 qh_h_addr_reg)
{
musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg);
}
static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
u8 qh_h_port_reg)
{
musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg);
}
static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_reg)
{
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR),
qh_addr_reg);
}
static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_reg)
{
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR),
qh_addr_reg);
}
static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
u8 qh_h_port_reg)
{
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT),
qh_h_port_reg);
}
static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR));
}
static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR));
}
static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT));
}
static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR));
}
static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR));
}
static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT));
}
#else /* CONFIG_BLACKFIN */
#define USB_BASE USB_FADDR
#define USB_OFFSET(reg) (reg - USB_BASE)
/*
* Common USB registers
*/
#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */
#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */
#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */
#define MUSB_INTRRX USB_OFFSET(USB_INTRRX)
#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE)
#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE)
#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */
#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */
#define MUSB_FRAME USB_OFFSET(USB_FRAME)
#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */
#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */
/* Get offset for a given FIFO from musb->mregs */
#define MUSB_FIFO_OFFSET(epnum) \
(USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8))
/*
* Additional Control Registers
*/
#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */
#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */
#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */
#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */
#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */
#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */
/* Offsets to endpoint registers */
#define MUSB_TXMAXP 0x00
#define MUSB_TXCSR 0x04
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
#define MUSB_RXMAXP 0x08
#define MUSB_RXCSR 0x0C
#define MUSB_RXCOUNT 0x10
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
#define MUSB_TXTYPE 0x14
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
#define MUSB_TXINTERVAL 0x18
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
#define MUSB_RXTYPE 0x1C
#define MUSB_RXINTERVAL 0x20
#define MUSB_TXCOUNT 0x28
/* Offsets to endpoint registers in indexed model (using INDEX register) */
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
(0x40 + (_offset))
/* Offsets to endpoint registers in flat models */
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
(USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset))
/* Not implemented - HW has separate Tx/Rx FIFO */
#define MUSB_TXCSR_MODE 0x0000
static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
{
}
static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
{
}
static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
{
}
static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
{
}
static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val)
{
}
static inline u8 musb_read_txfifosz(void __iomem *mbase)
{
return 0;
}
static inline u16 musb_read_txfifoadd(void __iomem *mbase)
{
return 0;
}
static inline u8 musb_read_rxfifosz(void __iomem *mbase)
{
return 0;
}
static inline u16 musb_read_rxfifoadd(void __iomem *mbase)
{
return 0;
}
static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase)
{
return 0;
}
static inline u8 musb_read_configdata(void __iomem *mbase)
{
return 0;
}
static inline u16 musb_read_hwvers(void __iomem *mbase)
{
/*
* This register is invisible on Blackfin, actually the MUSB
* RTL version of Blackfin is 1.9, so just harcode its value.
*/
return MUSB_HWVERS_1900;
}
static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
{
return NULL;
}
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
u8 qh_addr_req)
{
}
static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
u8 qh_h_addr_reg)
{
}
static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
u8 qh_h_port_reg)
{
}
static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_reg)
{
}
static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_reg)
{
}
static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
u8 qh_h_port_reg)
{
}
static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum)
{
return 0;
}
static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum)
{
return 0;
}
static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum)
{
return 0;
}
static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum)
{
return 0;
}
static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum)
{
return 0;
}
static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
{
return 0;
}
#endif /* CONFIG_BLACKFIN */
#endif /* __MUSB_REGS_H__ */

View File

@@ -0,0 +1,455 @@
#include <common.h>
#include <console.h>
#include <watchdog.h>
#ifdef CONFIG_ARCH_SUNXI
#include <asm/arch/usb_phy.h>
#endif
#include <asm/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <usb.h>
#include "linux-compat.h"
#include "usb-compat.h"
#include "musb_core.h"
#include "musb_host.h"
#include "musb_gadget.h"
#include "musb_uboot.h"
#ifdef CONFIG_USB_MUSB_HOST
struct int_queue {
struct usb_host_endpoint hep;
struct urb urb;
};
#ifndef CONFIG_DM_USB
struct musb_host_data musb_host;
#endif
static void musb_host_complete_urb(struct urb *urb)
{
urb->dev->status &= ~USB_ST_NOT_PROC;
urb->dev->act_len = urb->actual_length;
}
static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep,
struct usb_device *dev, int endpoint_type,
unsigned long pipe, void *buffer, int len,
struct devrequest *setup, int interval)
{
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
memset(urb, 0, sizeof(struct urb));
memset(hep, 0, sizeof(struct usb_host_endpoint));
INIT_LIST_HEAD(&hep->urb_list);
INIT_LIST_HEAD(&urb->urb_list);
urb->ep = hep;
urb->complete = musb_host_complete_urb;
urb->status = -EINPROGRESS;
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = buffer;
urb->transfer_dma = (unsigned long)buffer;
urb->transfer_buffer_length = len;
urb->setup_packet = (unsigned char *)setup;
urb->ep->desc.wMaxPacketSize =
__cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] :
dev->epmaxpacketout[epnum]);
urb->ep->desc.bmAttributes = endpoint_type;
urb->ep->desc.bEndpointAddress =
(is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum;
urb->ep->desc.bInterval = interval;
}
static int submit_urb(struct usb_hcd *hcd, struct urb *urb)
{
struct musb *host = hcd->hcd_priv;
int ret;
unsigned long timeout;
ret = musb_urb_enqueue(hcd, urb, 0);
if (ret < 0) {
printf("Failed to enqueue URB to controller\n");
return ret;
}
timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe);
do {
if (ctrlc())
return -EIO;
host->isr(0, host);
} while (urb->status == -EINPROGRESS &&
get_timer(0) < timeout);
if (urb->status == -EINPROGRESS)
musb_urb_dequeue(hcd, urb, -ETIME);
return urb->status;
}
static int _musb_submit_control_msg(struct musb_host_data *host,
struct usb_device *dev, unsigned long pipe,
void *buffer, int len, struct devrequest *setup)
{
construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_CONTROL,
pipe, buffer, len, setup, 0);
/* Fix speed for non hub-attached devices */
if (!usb_dev_get_parent(dev))
dev->speed = host->host_speed;
return submit_urb(&host->hcd, &host->urb);
}
static int _musb_submit_bulk_msg(struct musb_host_data *host,
struct usb_device *dev, unsigned long pipe, void *buffer, int len)
{
construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_BULK,
pipe, buffer, len, NULL, 0);
return submit_urb(&host->hcd, &host->urb);
}
static int _musb_submit_int_msg(struct musb_host_data *host,
struct usb_device *dev, unsigned long pipe,
void *buffer, int len, int interval)
{
construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_INT, pipe,
buffer, len, NULL, interval);
return submit_urb(&host->hcd, &host->urb);
}
static struct int_queue *_musb_create_int_queue(struct musb_host_data *host,
struct usb_device *dev, unsigned long pipe, int queuesize,
int elementsize, void *buffer, int interval)
{
struct int_queue *queue;
int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe);
if (queuesize != 1) {
printf("ERROR musb int-queues only support queuesize 1\n");
return NULL;
}
if (dev->int_pending & (1 << index)) {
printf("ERROR int-urb is already pending on pipe %lx\n", pipe);
return NULL;
}
queue = malloc(sizeof(*queue));
if (!queue)
return NULL;
construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT,
pipe, buffer, elementsize, NULL, interval);
ret = musb_urb_enqueue(&host->hcd, &queue->urb, 0);
if (ret < 0) {
printf("Failed to enqueue URB to controller\n");
free(queue);
return NULL;
}
dev->int_pending |= 1 << index;
return queue;
}
static int _musb_destroy_int_queue(struct musb_host_data *host,
struct usb_device *dev, struct int_queue *queue)
{
int index = usb_pipein(queue->urb.pipe) * 16 +
usb_pipeendpoint(queue->urb.pipe);
if (queue->urb.status == -EINPROGRESS)
musb_urb_dequeue(&host->hcd, &queue->urb, -ETIME);
dev->int_pending &= ~(1 << index);
free(queue);
return 0;
}
static void *_musb_poll_int_queue(struct musb_host_data *host,
struct usb_device *dev, struct int_queue *queue)
{
if (queue->urb.status != -EINPROGRESS)
return NULL; /* URB has already completed in a prev. poll */
host->host->isr(0, host->host);
if (queue->urb.status != -EINPROGRESS)
return queue->urb.transfer_buffer; /* Done */
return NULL; /* URB still pending */
}
static int _musb_reset_root_port(struct musb_host_data *host,
struct usb_device *dev)
{
void *mbase = host->host->mregs;
u8 power;
power = musb_readb(mbase, MUSB_POWER);
power &= 0xf0;
musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power);
mdelay(50);
#ifdef CONFIG_ARCH_SUNXI
/*
* sunxi phy has a bug and it will wrongly detect high speed squelch
* when clearing reset on low-speed devices, temporary disable
* squelch detection to work around this.
*/
sunxi_usb_phy_enable_squelch_detect(0, 0);
#endif
power = musb_readb(mbase, MUSB_POWER);
musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power);
#ifdef CONFIG_ARCH_SUNXI
sunxi_usb_phy_enable_squelch_detect(0, 1);
#endif
host->host->isr(0, host->host);
host->host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ?
USB_SPEED_HIGH :
(musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ?
USB_SPEED_FULL : USB_SPEED_LOW;
mdelay((host->host_speed == USB_SPEED_LOW) ? 200 : 50);
return 0;
}
int musb_lowlevel_init(struct musb_host_data *host)
{
void *mbase;
/* USB spec says it may take up to 1 second for a device to connect */
unsigned long timeout = get_timer(0) + 1000;
int ret;
if (!host->host) {
printf("MUSB host is not registered\n");
return -ENODEV;
}
ret = musb_start(host->host);
if (ret)
return ret;
mbase = host->host->mregs;
do {
if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM)
break;
} while (get_timer(0) < timeout);
if (get_timer(0) >= timeout) {
musb_stop(host->host);
return -ENODEV;
}
_musb_reset_root_port(host, NULL);
host->host->is_active = 1;
host->hcd.hcd_priv = host->host;
return 0;
}
#ifndef CONFIG_DM_USB
int usb_lowlevel_stop(int index)
{
if (!musb_host.host) {
printf("MUSB host is not registered\n");
return -ENODEV;
}
musb_stop(musb_host.host);
return 0;
}
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int length)
{
return _musb_submit_bulk_msg(&musb_host, dev, pipe, buffer, length);
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int length, struct devrequest *setup)
{
return _musb_submit_control_msg(&musb_host, dev, pipe, buffer, length, setup);
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int length, int interval)
{
return _musb_submit_int_msg(&musb_host, dev, pipe, buffer, length, interval);
}
struct int_queue *create_int_queue(struct usb_device *dev,
unsigned long pipe, int queuesize, int elementsize,
void *buffer, int interval)
{
return _musb_create_int_queue(&musb_host, dev, pipe, queuesize, elementsize,
buffer, interval);
}
void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
{
return _musb_poll_int_queue(&musb_host, dev, queue);
}
int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
{
return _musb_destroy_int_queue(&musb_host, dev, queue);
}
int usb_reset_root_port(struct usb_device *dev)
{
return _musb_reset_root_port(&musb_host, dev);
}
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
{
return musb_lowlevel_init(&musb_host);
}
#endif /* !CONFIG_DM_USB */
#ifdef CONFIG_DM_USB
static int musb_submit_control_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
struct devrequest *setup)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_submit_control_msg(host, udev, pipe, buffer, length, setup);
}
static int musb_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_submit_bulk_msg(host, udev, pipe, buffer, length);
}
static int musb_submit_int_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
int interval)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_submit_int_msg(host, udev, pipe, buffer, length, interval);
}
static struct int_queue *musb_create_int_queue(struct udevice *dev,
struct usb_device *udev, unsigned long pipe, int queuesize,
int elementsize, void *buffer, int interval)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_create_int_queue(host, udev, pipe, queuesize, elementsize,
buffer, interval);
}
static void *musb_poll_int_queue(struct udevice *dev, struct usb_device *udev,
struct int_queue *queue)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_poll_int_queue(host, udev, queue);
}
static int musb_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
struct int_queue *queue)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_destroy_int_queue(host, udev, queue);
}
static int musb_reset_root_port(struct udevice *dev, struct usb_device *udev)
{
struct musb_host_data *host = dev_get_priv(dev);
return _musb_reset_root_port(host, udev);
}
struct dm_usb_ops musb_usb_ops = {
.control = musb_submit_control_msg,
.bulk = musb_submit_bulk_msg,
.interrupt = musb_submit_int_msg,
.create_int_queue = musb_create_int_queue,
.poll_int_queue = musb_poll_int_queue,
.destroy_int_queue = musb_destroy_int_queue,
.reset_root_port = musb_reset_root_port,
};
#endif /* CONFIG_DM_USB */
#endif /* CONFIG_USB_MUSB_HOST */
#ifdef CONFIG_USB_MUSB_GADGET
static struct musb *gadget;
int usb_gadget_handle_interrupts(int index)
{
WATCHDOG_RESET();
if (!gadget || !gadget->isr)
return -EINVAL;
return gadget->isr(0, gadget);
}
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
int ret;
if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind ||
!driver->setup) {
printf("bad parameter.\n");
return -EINVAL;
}
if (!gadget) {
printf("Controller uninitialized\n");
return -ENXIO;
}
ret = musb_gadget_start(&gadget->g, driver);
if (ret < 0) {
printf("gadget_start failed with %d\n", ret);
return ret;
}
ret = driver->bind(&gadget->g);
if (ret < 0) {
printf("bind failed with %d\n", ret);
return ret;
}
return 0;
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
if (driver->disconnect)
driver->disconnect(&gadget->g);
if (driver->unbind)
driver->unbind(&gadget->g);
return 0;
}
#endif /* CONFIG_USB_MUSB_GADGET */
int musb_register(struct musb_hdrc_platform_data *plat, void *bdata,
void *ctl_regs)
{
struct musb **musbp;
switch (plat->mode) {
#if defined(CONFIG_USB_MUSB_HOST) && !defined(CONFIG_DM_USB)
case MUSB_HOST:
musbp = &musb_host.host;
break;
#endif
#ifdef CONFIG_USB_MUSB_GADGET
case MUSB_PERIPHERAL:
musbp = &gadget;
break;
#endif
default:
return -EINVAL;
}
*musbp = musb_init_controller(plat, (struct device *)bdata, ctl_regs);
if (!musbp) {
printf("Failed to init the controller\n");
return -EIO;
}
return 0;
}

View File

@@ -0,0 +1,28 @@
/*
* MUSB OTG driver u-boot specific functions
*
* Copyright © 2015 Hans de Goede <hdegoede@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __MUSB_UBOOT_H__
#define __MUSB_UBOOT_H__
#include <usb.h>
#include "linux-compat.h"
#include "usb-compat.h"
#include "musb_core.h"
struct musb_host_data {
struct musb *host;
struct usb_hcd hcd;
enum usb_device_speed host_speed;
struct usb_host_endpoint hep;
struct urb urb;
};
extern struct dm_usb_ops musb_usb_ops;
int musb_lowlevel_init(struct musb_host_data *host);
#endif

View File

@@ -0,0 +1,644 @@
/*
* Copyright (C) 2005-2007 by Texas Instruments
* Some code has been taken from tusb6010.c
* Copyrights for that are attributable to:
* Copyright (C) 2006 Nokia Corporation
* Tony Lindgren <tony@atomide.com>
*
* This file is part of the Inventra Controller Driver for Linux.
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __UBOOT__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <linux/usb/musb-omap.h>
#else
#include <common.h>
#include <asm/omap_common.h>
#include <asm/omap_musb.h>
#include <twl4030.h>
#include <twl6030.h>
#include "linux-compat.h"
#endif
#include "musb_core.h"
#include "omap2430.h"
#ifndef __UBOOT__
struct omap2430_glue {
struct device *dev;
struct platform_device *musb;
enum omap_musb_vbus_id_status status;
struct work_struct omap_musb_mailbox_work;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
struct omap2430_glue *_glue;
static struct timer_list musb_idle_timer;
static void musb_do_idle(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
unsigned long flags;
u8 power;
u8 devctl;
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
case OTG_STATE_A_WAIT_BCON:
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
musb->xceiv->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
case OTG_STATE_A_SUSPEND:
/* finish RESUME signaling? */
if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
power = musb_readb(musb->mregs, MUSB_POWER);
power &= ~MUSB_POWER_RESUME;
dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power);
musb_writeb(musb->mregs, MUSB_POWER, power);
musb->is_active = 1;
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
| MUSB_PORT_STAT_RESUME);
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
usb_hcd_poll_rh_status(musb_to_hcd(musb));
/* NOTE: it might really be A_WAIT_BCON ... */
musb->xceiv->state = OTG_STATE_A_HOST;
}
break;
case OTG_STATE_A_HOST:
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
musb->xceiv->state = OTG_STATE_B_IDLE;
else
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
default:
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
{
unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
static unsigned long last_timer;
if (timeout == 0)
timeout = default_timeout;
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || ((musb->a_wait_bcon == 0)
&& (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
otg_state_string(musb->xceiv->state));
del_timer(&musb_idle_timer);
last_timer = jiffies;
return;
}
if (time_after(last_timer, timeout)) {
if (!timer_pending(&musb_idle_timer))
last_timer = timeout;
else {
dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n");
return;
}
}
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
otg_state_string(musb->xceiv->state),
(unsigned long)jiffies_to_msecs(timeout - jiffies));
mod_timer(&musb_idle_timer, timeout);
}
static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
{
struct usb_otg *otg = musb->xceiv->otg;
u8 devctl;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
int ret = 1;
/* HDRC controls CPEN, but beware current surges during device
* connect. They can trigger transient overcurrent conditions
* that must be ignored.
*/
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (is_on) {
if (musb->xceiv->state == OTG_STATE_A_IDLE) {
/* start the session */
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
/*
* Wait for the musb to set as A device to enable the
* VBUS
*/
while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
cpu_relax();
if (time_after(jiffies, timeout)) {
dev_err(musb->controller,
"configured as A device timeout");
ret = -EINVAL;
break;
}
}
if (ret && otg->set_vbus)
otg_set_vbus(otg, 1);
} else {
musb->is_active = 1;
otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
}
} else {
musb->is_active = 0;
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
* jumping right to B_IDLE...
*/
otg->default_a = 0;
musb->xceiv->state = OTG_STATE_B_IDLE;
devctl &= ~MUSB_DEVCTL_SESSION;
MUSB_DEV_MODE(musb);
}
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
dev_dbg(musb->controller, "VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ "\n",
otg_state_string(musb->xceiv->state),
musb_readb(musb->mregs, MUSB_DEVCTL));
}
static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode)
{
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
return 0;
}
#endif
static inline void omap2430_low_level_exit(struct musb *musb)
{
u32 l;
/* in any role */
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
l |= ENABLEFORCE; /* enable MSTANDBY */
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
}
static inline void omap2430_low_level_init(struct musb *musb)
{
u32 l;
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
l &= ~ENABLEFORCE; /* disable MSTANDBY */
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
}
#ifndef __UBOOT__
void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
{
struct omap2430_glue *glue = _glue;
struct musb *musb = glue_to_musb(glue);
glue->status = status;
if (!musb) {
dev_err(glue->dev, "musb core is not yet ready\n");
return;
}
schedule_work(&glue->omap_musb_mailbox_work);
}
EXPORT_SYMBOL_GPL(omap_musb_mailbox);
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
struct musb *musb = glue_to_musb(glue);
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *pdata = dev->platform_data;
struct omap_musb_board_data *data = pdata->board_data;
struct usb_otg *otg = musb->xceiv->otg;
switch (glue->status) {
case OMAP_MUSB_ID_GROUND:
dev_dbg(dev, "ID GND\n");
otg->default_a = true;
musb->xceiv->state = OTG_STATE_A_IDLE;
musb->xceiv->last_event = USB_EVENT_ID;
if (!is_otg_enabled(musb) || musb->gadget_driver) {
pm_runtime_get_sync(dev);
usb_phy_init(musb->xceiv);
omap2430_musb_set_vbus(musb, 1);
}
break;
case OMAP_MUSB_VBUS_VALID:
dev_dbg(dev, "VBUS Connect\n");
otg->default_a = false;
musb->xceiv->state = OTG_STATE_B_IDLE;
musb->xceiv->last_event = USB_EVENT_VBUS;
if (musb->gadget_driver)
pm_runtime_get_sync(dev);
usb_phy_init(musb->xceiv);
break;
case OMAP_MUSB_ID_FLOAT:
case OMAP_MUSB_VBUS_OFF:
dev_dbg(dev, "VBUS Disconnect\n");
musb->xceiv->last_event = USB_EVENT_NONE;
if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
if (musb->gadget_driver) {
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
if (data->interface_type == MUSB_INTERFACE_UTMI) {
if (musb->xceiv->otg->set_vbus)
otg_set_vbus(musb->xceiv->otg, 0);
}
usb_phy_shutdown(musb->xceiv);
break;
default:
dev_dbg(dev, "ID float\n");
}
}
static void omap_musb_mailbox_work(struct work_struct *mailbox_work)
{
struct omap2430_glue *glue = container_of(mailbox_work,
struct omap2430_glue, omap_musb_mailbox_work);
omap_musb_set_mailbox(glue);
}
#endif
static int omap2430_musb_init(struct musb *musb)
{
u32 l;
int status = 0;
unsigned long int start;
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
#else
struct omap_musb_board_data *data =
(struct omap_musb_board_data *)musb->controller;
#endif
/* Reset the controller */
musb_writel(musb->mregs, OTG_SYSCONFIG, SOFTRST);
start = get_timer(0);
while (1) {
l = musb_readl(musb->mregs, OTG_SYSCONFIG);
if ((l & SOFTRST) == 0)
break;
if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) {
dev_err(musb->controller, "MUSB reset is taking too long\n");
return -ENODEV;
}
}
#ifndef __UBOOT__
/* We require some kind of external transceiver, hooked
* up through ULPI. TWL4030-family PMICs include one,
* which needs a driver, drivers aren't always needed.
*/
musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(musb->xceiv)) {
pr_err("HS USB OTG: no transceiver configured\n");
return -ENODEV;
}
status = pm_runtime_get_sync(dev);
if (status < 0) {
dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status);
goto err1;
}
#endif
l = musb_readl(musb->mregs, OTG_INTERFSEL);
if (data->interface_type == MUSB_INTERFACE_UTMI) {
/* OMAP4 uses Internal PHY GS70 which uses UTMI interface */
l &= ~ULPI_12PIN; /* Disable ULPI */
l |= UTMI_8BIT; /* Enable UTMI */
} else {
l |= ULPI_12PIN;
}
musb_writel(musb->mregs, OTG_INTERFSEL, l);
pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
"sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
musb_readl(musb->mregs, OTG_REVISION),
musb_readl(musb->mregs, OTG_SYSCONFIG),
musb_readl(musb->mregs, OTG_SYSSTATUS),
musb_readl(musb->mregs, OTG_INTERFSEL),
musb_readl(musb->mregs, OTG_SIMENABLE));
#ifndef __UBOOT__
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
if (glue->status != OMAP_MUSB_UNKNOWN)
omap_musb_set_mailbox(glue);
pm_runtime_put_noidle(musb->controller);
#endif
return 0;
err1:
return status;
}
#ifndef __UBOOT__
static void omap2430_musb_enable(struct musb *musb)
#else
static int omap2430_musb_enable(struct musb *musb)
#endif
{
#ifndef __UBOOT__
u8 devctl;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
struct musb_hdrc_platform_data *pdata = dev->platform_data;
struct omap_musb_board_data *data = pdata->board_data;
switch (glue->status) {
case OMAP_MUSB_ID_GROUND:
usb_phy_init(musb->xceiv);
if (data->interface_type != MUSB_INTERFACE_UTMI)
break;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
/* start the session */
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
while (musb_readb(musb->mregs, MUSB_DEVCTL) &
MUSB_DEVCTL_BDEVICE) {
cpu_relax();
if (time_after(jiffies, timeout)) {
dev_err(dev, "configured as A device timeout");
break;
}
}
break;
case OMAP_MUSB_VBUS_VALID:
usb_phy_init(musb->xceiv);
break;
default:
break;
}
#else
#ifdef CONFIG_TWL4030_USB
if (twl4030_usb_ulpi_init()) {
serial_printf("ERROR: %s Could not initialize PHY\n",
__PRETTY_FUNCTION__);
}
#endif
#ifdef CONFIG_TWL6030_POWER
twl6030_usb_device_settings();
#endif
#ifdef CONFIG_OMAP4430
u32 *usbotghs_control = (u32 *)((*ctrl)->control_usbotghs_ctrl);
*usbotghs_control = USBOTGHS_CONTROL_AVALID |
USBOTGHS_CONTROL_VBUSVALID | USBOTGHS_CONTROL_IDDIG;
#endif
return 0;
#endif
}
static void omap2430_musb_disable(struct musb *musb)
{
#ifndef __UBOOT__
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
if (glue->status != OMAP_MUSB_UNKNOWN)
usb_phy_shutdown(musb->xceiv);
#endif
}
static int omap2430_musb_exit(struct musb *musb)
{
del_timer_sync(&musb_idle_timer);
omap2430_low_level_exit(musb);
return 0;
}
#ifndef __UBOOT__
static const struct musb_platform_ops omap2430_ops = {
#else
const struct musb_platform_ops omap2430_ops = {
#endif
.init = omap2430_musb_init,
.exit = omap2430_musb_exit,
#ifndef __UBOOT__
.set_mode = omap2430_musb_set_mode,
.try_idle = omap2430_musb_try_idle,
.set_vbus = omap2430_musb_set_vbus,
#endif
.enable = omap2430_musb_enable,
.disable = omap2430_musb_disable,
};
#ifndef __UBOOT__
static u64 omap2430_dmamask = DMA_BIT_MASK(32);
static int __devinit omap2430_probe(struct platform_device *pdev)
{
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
struct platform_device *musb;
struct omap2430_glue *glue;
int ret = -ENOMEM;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
}
musb = platform_device_alloc("musb-hdrc", -1);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
goto err0;
}
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &omap2430_dmamask;
musb->dev.coherent_dma_mask = omap2430_dmamask;
glue->dev = &pdev->dev;
glue->musb = musb;
glue->status = OMAP_MUSB_UNKNOWN;
pdata->platform_ops = &omap2430_ops;
platform_set_drvdata(pdev, glue);
/*
* REVISIT if we ever have two instances of the wrapper, we will be
* in big trouble
*/
_glue = glue;
INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
ret = platform_device_add_resources(musb, pdev->resource,
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
goto err1;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
goto err1;
}
pm_runtime_enable(&pdev->dev);
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
goto err1;
}
return 0;
err1:
platform_device_put(musb);
err0:
return ret;
}
static int __devexit omap2430_remove(struct platform_device *pdev)
{
struct omap2430_glue *glue = platform_get_drvdata(pdev);
cancel_work_sync(&glue->omap_musb_mailbox_work);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
return 0;
}
#ifdef CONFIG_PM
static int omap2430_runtime_suspend(struct device *dev)
{
struct omap2430_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
if (musb) {
musb->context.otg_interfsel = musb_readl(musb->mregs,
OTG_INTERFSEL);
omap2430_low_level_exit(musb);
usb_phy_set_suspend(musb->xceiv, 1);
}
return 0;
}
static int omap2430_runtime_resume(struct device *dev)
{
struct omap2430_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
if (musb) {
omap2430_low_level_init(musb);
musb_writel(musb->mregs, OTG_INTERFSEL,
musb->context.otg_interfsel);
usb_phy_set_suspend(musb->xceiv, 0);
}
return 0;
}
static struct dev_pm_ops omap2430_pm_ops = {
.runtime_suspend = omap2430_runtime_suspend,
.runtime_resume = omap2430_runtime_resume,
};
#define DEV_PM_OPS (&omap2430_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
static struct platform_driver omap2430_driver = {
.probe = omap2430_probe,
.remove = __devexit_p(omap2430_remove),
.driver = {
.name = "musb-omap2430",
.pm = DEV_PM_OPS,
},
};
MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("GPL v2");
static int __init omap2430_init(void)
{
return platform_driver_register(&omap2430_driver);
}
subsys_initcall(omap2430_init);
static void __exit omap2430_exit(void)
{
platform_driver_unregister(&omap2430_driver);
}
module_exit(omap2430_exit);
#endif

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2005-2006 by Texas Instruments
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __MUSB_OMAP243X_H__
#define __MUSB_OMAP243X_H__
#ifndef __UBOOT__
#include <plat/usb.h>
#else
#undef RESETDONE
#endif
/*
* OMAP2430-specific definitions
*/
#define OTG_REVISION 0x400
#define OTG_SYSCONFIG 0x404
# define MIDLEMODE 12 /* bit position */
# define FORCESTDBY (0 << MIDLEMODE)
# define NOSTDBY (1 << MIDLEMODE)
# define SMARTSTDBY (2 << MIDLEMODE)
# define SIDLEMODE 3 /* bit position */
# define FORCEIDLE (0 << SIDLEMODE)
# define NOIDLE (1 << SIDLEMODE)
# define SMARTIDLE (2 << SIDLEMODE)
# define ENABLEWAKEUP (1 << 2)
# define SOFTRST (1 << 1)
# define AUTOIDLE (1 << 0)
#define OTG_SYSSTATUS 0x408
# define RESETDONE (1 << 0)
#define OTG_INTERFSEL 0x40c
# define EXTCP (1 << 2)
# define PHYSEL 0 /* bit position */
# define UTMI_8BIT (0 << PHYSEL)
# define ULPI_12PIN (1 << PHYSEL)
# define ULPI_8PIN (2 << PHYSEL)
#define OTG_SIMENABLE 0x410
# define TM1 (1 << 0)
#define OTG_FORCESTDBY 0x414
# define ENABLEFORCE (1 << 0)
/*
* OMAP4-specific definitions
*/
#define USBOTGHS_CONTROL_AVALID (1 << 0)
#define USBOTGHS_CONTROL_VBUSVALID (1 << 2)
#define USBOTGHS_CONTROL_IDDIG (1 << 4)
#endif /* __MUSB_OMAP243X_H__ */

View File

@@ -0,0 +1,288 @@
/*
* Microchip PIC32 MUSB "glue layer"
*
* Copyright (C) 2015, Microchip Technology Inc.
* Cristian Birsan <cristian.birsan@microchip.com>
* Purna Chandra Mandal <purna.mandal@microchip.com>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Based on the dsps "glue layer" code.
*/
#include <common.h>
#include <linux/usb/musb.h>
#include "linux-compat.h"
#include "musb_core.h"
#include "musb_uboot.h"
DECLARE_GLOBAL_DATA_PTR;
#define PIC32_TX_EP_MASK 0x0f /* EP0 + 7 Tx EPs */
#define PIC32_RX_EP_MASK 0x0e /* 7 Rx EPs */
#define MUSB_SOFTRST 0x7f
#define MUSB_SOFTRST_NRST BIT(0)
#define MUSB_SOFTRST_NRSTX BIT(1)
#define USBCRCON 0
#define USBCRCON_USBWKUPEN BIT(0) /* Enable Wakeup Interrupt */
#define USBCRCON_USBRIE BIT(1) /* Enable Remote resume Interrupt */
#define USBCRCON_USBIE BIT(2) /* Enable USB General interrupt */
#define USBCRCON_SENDMONEN BIT(3) /* Enable Session End VBUS monitoring */
#define USBCRCON_BSVALMONEN BIT(4) /* Enable B-Device VBUS monitoring */
#define USBCRCON_ASVALMONEN BIT(5) /* Enable A-Device VBUS monitoring */
#define USBCRCON_VBUSMONEN BIT(6) /* Enable VBUS monitoring */
#define USBCRCON_PHYIDEN BIT(7) /* PHY ID monitoring enable */
#define USBCRCON_USBIDVAL BIT(8) /* USB ID value */
#define USBCRCON_USBIDOVEN BIT(9) /* USB ID override enable */
#define USBCRCON_USBWK BIT(24) /* USB Wakeup Status */
#define USBCRCON_USBRF BIT(25) /* USB Resume Status */
#define USBCRCON_USBIF BIT(26) /* USB General Interrupt Status */
/* PIC32 controller data */
struct pic32_musb_data {
struct musb_host_data mdata;
struct device dev;
void __iomem *musb_glue;
};
#define to_pic32_musb_data(d) \
container_of(d, struct pic32_musb_data, dev)
static void pic32_musb_disable(struct musb *musb)
{
/* no way to shut the controller */
}
static int pic32_musb_enable(struct musb *musb)
{
/* soft reset by NRSTx */
musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX);
/* set mode */
musb_platform_set_mode(musb, musb->board_mode);
return 0;
}
static irqreturn_t pic32_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
irqreturn_t ret = IRQ_NONE;
u32 epintr, usbintr;
/* ack usb core interrupts */
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
if (musb->int_usb)
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
/* ack endpoint interrupts */
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX) & PIC32_RX_EP_MASK;
if (musb->int_rx)
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX) & PIC32_TX_EP_MASK;
if (musb->int_tx)
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
/* drop spurious RX and TX if device is disconnected */
if (musb->int_usb & MUSB_INTR_DISCONNECT) {
musb->int_tx = 0;
musb->int_rx = 0;
}
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret = musb_interrupt(musb);
return ret;
}
static int pic32_musb_set_mode(struct musb *musb, u8 mode)
{
struct device *dev = musb->controller;
struct pic32_musb_data *pdata = to_pic32_musb_data(dev);
switch (mode) {
case MUSB_HOST:
clrsetbits_le32(pdata->musb_glue + USBCRCON,
USBCRCON_USBIDVAL, USBCRCON_USBIDOVEN);
break;
case MUSB_PERIPHERAL:
setbits_le32(pdata->musb_glue + USBCRCON,
USBCRCON_USBIDVAL | USBCRCON_USBIDOVEN);
break;
case MUSB_OTG:
dev_err(dev, "support for OTG is unimplemented\n");
break;
default:
dev_err(dev, "unsupported mode %d\n", mode);
return -EINVAL;
}
return 0;
}
static int pic32_musb_init(struct musb *musb)
{
struct pic32_musb_data *pdata = to_pic32_musb_data(musb->controller);
u32 ctrl, hwvers;
u8 power;
/* Returns zero if not clocked */
hwvers = musb_read_hwvers(musb->mregs);
if (!hwvers)
return -ENODEV;
/* Reset the musb */
power = musb_readb(musb->mregs, MUSB_POWER);
power = power | MUSB_POWER_RESET;
musb_writeb(musb->mregs, MUSB_POWER, power);
mdelay(100);
/* Start the on-chip PHY and its PLL. */
power = power & ~MUSB_POWER_RESET;
musb_writeb(musb->mregs, MUSB_POWER, power);
musb->isr = pic32_interrupt;
ctrl = USBCRCON_USBIF | USBCRCON_USBRF |
USBCRCON_USBWK | USBCRCON_USBIDOVEN |
USBCRCON_PHYIDEN | USBCRCON_USBIE |
USBCRCON_USBRIE | USBCRCON_USBWKUPEN |
USBCRCON_VBUSMONEN;
writel(ctrl, pdata->musb_glue + USBCRCON);
return 0;
}
/* PIC32 supports only 32bit read operation */
void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
void __iomem *fifo = hw_ep->fifo;
u32 val, rem = len % 4;
/* USB stack ensures dst is always 32bit aligned. */
readsl(fifo, dst, len / 4);
if (rem) {
dst += len & ~0x03;
val = musb_readl(fifo, 0);
memcpy(dst, &val, rem);
}
}
const struct musb_platform_ops pic32_musb_ops = {
.init = pic32_musb_init,
.set_mode = pic32_musb_set_mode,
.disable = pic32_musb_disable,
.enable = pic32_musb_enable,
};
/* PIC32 default FIFO config - fits in 8KB */
static struct musb_fifo_cfg pic32_musb_fifo_config[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, },
};
static struct musb_hdrc_config pic32_musb_config = {
.fifo_cfg = pic32_musb_fifo_config,
.fifo_cfg_size = ARRAY_SIZE(pic32_musb_fifo_config),
.multipoint = 1,
.dyn_fifo = 1,
.num_eps = 8,
.ram_bits = 11,
};
/* PIC32 has one MUSB controller which can be host or gadget */
static struct musb_hdrc_platform_data pic32_musb_plat = {
.mode = MUSB_HOST,
.config = &pic32_musb_config,
.power = 250, /* 500mA */
.platform_ops = &pic32_musb_ops,
};
static int musb_usb_probe(struct udevice *dev)
{
struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
struct pic32_musb_data *pdata = dev_get_priv(dev);
struct musb_host_data *mdata = &pdata->mdata;
struct fdt_resource mc, glue;
void *fdt = (void *)gd->fdt_blob;
int node = dev->of_offset;
void __iomem *mregs;
int ret;
priv->desc_before_addr = true;
ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
"mc", &mc);
if (ret < 0) {
printf("pic32-musb: resource \"mc\" not found\n");
return ret;
}
ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
"control", &glue);
if (ret < 0) {
printf("pic32-musb: resource \"control\" not found\n");
return ret;
}
mregs = ioremap(mc.start, fdt_resource_size(&mc));
pdata->musb_glue = ioremap(glue.start, fdt_resource_size(&glue));
/* init controller */
#ifdef CONFIG_USB_MUSB_HOST
mdata->host = musb_init_controller(&pic32_musb_plat,
&pdata->dev, mregs);
if (!mdata->host)
return -EIO;
ret = musb_lowlevel_init(mdata);
#else
pic32_musb_plat.mode = MUSB_PERIPHERAL;
ret = musb_register(&pic32_musb_plat, &pdata->dev, mregs);
#endif
if (ret == 0)
printf("PIC32 MUSB OTG\n");
return ret;
}
static int musb_usb_remove(struct udevice *dev)
{
struct pic32_musb_data *pdata = dev_get_priv(dev);
musb_stop(pdata->mdata.host);
return 0;
}
static const struct udevice_id pic32_musb_ids[] = {
{ .compatible = "microchip,pic32mzda-usb" },
{ }
};
U_BOOT_DRIVER(usb_musb) = {
.name = "pic32-musb",
.id = UCLASS_USB,
.of_match = pic32_musb_ids,
.probe = musb_usb_probe,
.remove = musb_usb_remove,
#ifdef CONFIG_USB_MUSB_HOST
.ops = &musb_usb_ops,
#endif
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
.priv_auto_alloc_size = sizeof(struct pic32_musb_data),
};

View File

@@ -0,0 +1,382 @@
/*
* Allwinner SUNXI "glue layer"
*
* Copyright © 2015 Hans de Goede <hdegoede@redhat.com>
* Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi>
*
* Based on the sw_usb "Allwinner OTG Dual Role Controller" code.
* Copyright 2007-2012 (C) Allwinner Technology Co., Ltd.
* javen <javen@allwinnertech.com>
*
* Based on the DA8xx "glue layer" code.
* Copyright (c) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
* Copyright (C) 2005-2006 by Texas Instruments
*
* This file is part of the Inventra Controller Driver for Linux.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <asm/arch/gpio.h>
#include <asm/arch/usb_phy.h>
#include <asm-generic/gpio.h>
#include <dm/lists.h>
#include <dm/root.h>
#include <linux/usb/musb.h>
#include "linux-compat.h"
#include "musb_core.h"
#include "musb_uboot.h"
/******************************************************************************
******************************************************************************
* From the Allwinner driver
******************************************************************************
******************************************************************************/
/******************************************************************************
* From include/sunxi_usb_bsp.h
******************************************************************************/
/* reg offsets */
#define USBC_REG_o_ISCR 0x0400
#define USBC_REG_o_PHYCTL 0x0404
#define USBC_REG_o_PHYBIST 0x0408
#define USBC_REG_o_PHYTUNE 0x040c
#define USBC_REG_o_VEND0 0x0043
/* Interface Status and Control */
#define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30
#define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29
#define USBC_BP_ISCR_EXT_ID_STATUS 28
#define USBC_BP_ISCR_EXT_DM_STATUS 27
#define USBC_BP_ISCR_EXT_DP_STATUS 26
#define USBC_BP_ISCR_MERGED_VBUS_STATUS 25
#define USBC_BP_ISCR_MERGED_ID_STATUS 24
#define USBC_BP_ISCR_ID_PULLUP_EN 17
#define USBC_BP_ISCR_DPDM_PULLUP_EN 16
#define USBC_BP_ISCR_FORCE_ID 14
#define USBC_BP_ISCR_FORCE_VBUS_VALID 12
#define USBC_BP_ISCR_VBUS_VALID_SRC 10
#define USBC_BP_ISCR_HOSC_EN 7
#define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6
#define USBC_BP_ISCR_ID_CHANGE_DETECT 5
#define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4
#define USBC_BP_ISCR_IRQ_ENABLE 3
#define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2
#define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1
#define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0
/******************************************************************************
* From usbc/usbc.c
******************************************************************************/
static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val)
{
u32 temp = reg_val;
temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT);
temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT);
temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT);
return temp;
}
static void USBC_EnableIdPullUp(__iomem void *base)
{
u32 reg_val;
reg_val = musb_readl(base, USBC_REG_o_ISCR);
reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN);
reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
musb_writel(base, USBC_REG_o_ISCR, reg_val);
}
static void USBC_EnableDpDmPullUp(__iomem void *base)
{
u32 reg_val;
reg_val = musb_readl(base, USBC_REG_o_ISCR);
reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN);
reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
musb_writel(base, USBC_REG_o_ISCR, reg_val);
}
static void USBC_ForceIdToLow(__iomem void *base)
{
u32 reg_val;
reg_val = musb_readl(base, USBC_REG_o_ISCR);
reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID);
reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID);
reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
musb_writel(base, USBC_REG_o_ISCR, reg_val);
}
static void USBC_ForceIdToHigh(__iomem void *base)
{
u32 reg_val;
reg_val = musb_readl(base, USBC_REG_o_ISCR);
reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID);
reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID);
reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
musb_writel(base, USBC_REG_o_ISCR, reg_val);
}
static void USBC_ForceVbusValidToLow(__iomem void *base)
{
u32 reg_val;
reg_val = musb_readl(base, USBC_REG_o_ISCR);
reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
reg_val |= (0x02 << USBC_BP_ISCR_FORCE_VBUS_VALID);
reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
musb_writel(base, USBC_REG_o_ISCR, reg_val);
}
static void USBC_ForceVbusValidToHigh(__iomem void *base)
{
u32 reg_val;
reg_val = musb_readl(base, USBC_REG_o_ISCR);
reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
musb_writel(base, USBC_REG_o_ISCR, reg_val);
}
static void USBC_ConfigFIFO_Base(void)
{
u32 reg_value;
/* config usb fifo, 8kb mode */
reg_value = readl(SUNXI_SRAMC_BASE + 0x04);
reg_value &= ~(0x03 << 0);
reg_value |= (1 << 0);
writel(reg_value, SUNXI_SRAMC_BASE + 0x04);
}
/******************************************************************************
* Needed for the DFU polling magic
******************************************************************************/
static u8 last_int_usb;
bool dfu_usb_get_reset(void)
{
return !!(last_int_usb & MUSB_INTR_RESET);
}
/******************************************************************************
* MUSB Glue code
******************************************************************************/
static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
{
struct musb *musb = __hci;
irqreturn_t retval = IRQ_NONE;
/* read and flush interrupts */
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
last_int_usb = musb->int_usb;
if (musb->int_usb)
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
if (musb->int_tx)
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
if (musb->int_rx)
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
if (musb->int_usb || musb->int_tx || musb->int_rx)
retval |= musb_interrupt(musb);
return retval;
}
/* musb_core does not call enable / disable in a balanced manner <sigh> */
static bool enabled = false;
static struct musb *sunxi_musb;
static int sunxi_musb_enable(struct musb *musb)
{
pr_debug("%s():\n", __func__);
musb_ep_select(musb->mregs, 0);
musb_writeb(musb->mregs, MUSB_FADDR, 0);
if (enabled)
return 0;
/* select PIO mode */
musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0);
if (is_host_enabled(musb)) {
int id = sunxi_usb_phy_id_detect(0);
if (id == 1 && sunxi_usb_phy_power_is_on(0))
sunxi_usb_phy_power_off(0);
if (!sunxi_usb_phy_power_is_on(0)) {
int vbus = sunxi_usb_phy_vbus_detect(0);
if (vbus == 1) {
printf("A charger is plugged into the OTG: ");
return -ENODEV;
}
}
if (id == 1) {
printf("No host cable detected: ");
return -ENODEV;
}
if (!sunxi_usb_phy_power_is_on(0))
sunxi_usb_phy_power_on(0);
}
USBC_ForceVbusValidToHigh(musb->mregs);
enabled = true;
return 0;
}
static void sunxi_musb_disable(struct musb *musb)
{
pr_debug("%s():\n", __func__);
if (!enabled)
return;
USBC_ForceVbusValidToLow(musb->mregs);
mdelay(200); /* Wait for the current session to timeout */
enabled = false;
}
static int sunxi_musb_init(struct musb *musb)
{
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
pr_debug("%s():\n", __func__);
musb->isr = sunxi_musb_interrupt;
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0);
#ifdef CONFIG_SUNXI_GEN_SUN6I
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0);
#endif
sunxi_usb_phy_init(0);
USBC_ConfigFIFO_Base();
USBC_EnableDpDmPullUp(musb->mregs);
USBC_EnableIdPullUp(musb->mregs);
if (is_host_enabled(musb)) {
/* Host mode */
USBC_ForceIdToLow(musb->mregs);
} else {
/* Peripheral mode */
USBC_ForceIdToHigh(musb->mregs);
}
USBC_ForceVbusValidToHigh(musb->mregs);
return 0;
}
static const struct musb_platform_ops sunxi_musb_ops = {
.init = sunxi_musb_init,
.enable = sunxi_musb_enable,
.disable = sunxi_musb_disable,
};
static struct musb_hdrc_config musb_config = {
.multipoint = 1,
.dyn_fifo = 1,
.num_eps = 6,
.ram_bits = 11,
};
static struct musb_hdrc_platform_data musb_plat = {
#if defined(CONFIG_USB_MUSB_HOST)
.mode = MUSB_HOST,
#else
.mode = MUSB_PERIPHERAL,
#endif
.config = &musb_config,
.power = 250,
.platform_ops = &sunxi_musb_ops,
};
#ifdef CONFIG_USB_MUSB_HOST
int musb_usb_probe(struct udevice *dev)
{
struct musb_host_data *host = dev_get_priv(dev);
struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
int ret;
priv->desc_before_addr = true;
if (!sunxi_musb) {
sunxi_musb = musb_init_controller(&musb_plat, NULL,
(void *)SUNXI_USB0_BASE);
}
host->host = sunxi_musb;
if (!host->host)
return -EIO;
ret = musb_lowlevel_init(host);
if (ret == 0)
printf("MUSB OTG\n");
return ret;
}
int musb_usb_remove(struct udevice *dev)
{
struct musb_host_data *host = dev_get_priv(dev);
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
musb_stop(host->host);
sunxi_usb_phy_exit(0);
#ifdef CONFIG_SUNXI_GEN_SUN6I
clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0);
#endif
clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0);
return 0;
}
U_BOOT_DRIVER(usb_musb) = {
.name = "sunxi-musb",
.id = UCLASS_USB,
.probe = musb_usb_probe,
.remove = musb_usb_remove,
.ops = &musb_usb_ops,
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
.priv_auto_alloc_size = sizeof(struct musb_host_data),
};
#endif
void sunxi_musb_board_init(void)
{
#ifdef CONFIG_USB_MUSB_HOST
struct udevice *dev;
/*
* Bind the driver directly for now as musb linux kernel support is
* still pending upstream so our dts files do not have the necessary
* nodes yet. TODO: Remove this as soon as the dts nodes are in place
* and bind by compatible instead.
*/
device_bind_driver(dm_root(), "sunxi-musb", "sunxi-musb", &dev);
#else
musb_register(&musb_plat, NULL, (void *)SUNXI_USB0_BASE);
#endif
}

View File

@@ -0,0 +1,106 @@
#ifndef __USB_COMPAT_H__
#define __USB_COMPAT_H__
#include <dm.h>
#include "usb.h"
struct usb_hcd {
void *hcd_priv;
};
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct list_head urb_list;
void *hcpriv;
};
/*
* urb->transfer_flags:
*
* Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb().
*/
#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */
#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */
struct urb;
typedef void (*usb_complete_t)(struct urb *);
struct urb {
void *hcpriv; /* private data for host controller */
struct list_head urb_list; /* list head for use by the urb's
* current owner */
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
int start_frame; /* (modify) start frame (ISO) */
usb_complete_t complete; /* (in) completion routine */
};
#define usb_hcd_link_urb_to_ep(hcd, urb) ({ \
int ret = 0; \
list_add_tail(&urb->urb_list, &urb->ep->urb_list); \
ret; })
#define usb_hcd_unlink_urb_from_ep(hcd, urb) list_del_init(&urb->urb_list)
#define usb_hcd_check_unlink_urb(hdc, urb, status) 0
static inline void usb_hcd_giveback_urb(struct usb_hcd *hcd,
struct urb *urb,
int status)
{
urb->status = status;
if (urb->complete)
urb->complete(urb);
}
static inline int usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd,
struct urb *urb)
{
/* TODO: add cache invalidation here */
return 0;
}
#ifdef CONFIG_DM_USB
static inline struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
struct udevice *parent = udev->dev->parent;
/*
* When called from usb-uclass.c: usb_scan_device() udev->dev points
* to the parent udevice, not the actual udevice belonging to the
* udev as the device is not instantiated yet.
*
* If dev is an usb-bus, then we are called from usb_scan_device() for
* an usb-device plugged directly into the root port, return NULL.
*/
if (device_get_uclass_id(udev->dev) == UCLASS_USB)
return NULL;
/*
* If these 2 are not the same we are being called from
* usb_scan_device() and udev itself is the parent.
*/
if (dev_get_parent_priv(udev->dev) != udev)
return udev;
/* We are being called normally, use the parent pointer */
if (device_get_uclass_id(parent) == UCLASS_USB_HUB)
return dev_get_parent_priv(parent);
return NULL;
}
#else
static inline struct usb_device *usb_dev_get_parent(struct usb_device *dev)
{
return dev->parent;
}
#endif
#endif /* __USB_COMPAT_H__ */