usb: musb-new: Add glue driver for ST-Ericsson Ux500

The ST-Ericsson DB8500 SoC contains a MUSB OTG controller which
supports both host and gadget mode. For some reason there is
nothing special about it - add a simple glue driver for Ux500
that literally just sets up MUSB together with a generic PHY.
There are no SoC-specific registers etc needed to make USB work.

The new Ux500 glue driver is only tested to work with DM_USB
and DM_USB_GADGET. Both host and gadget mode work fine on
the u8500 "stemmy" board that is already present in U-Boot.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
This commit is contained in:
Stephan Gerhold 2021-07-08 20:33:50 +02:00 committed by Tom Rini
parent 4559df9e81
commit 845d9cf61c
4 changed files with 191 additions and 2 deletions

View File

@ -72,6 +72,15 @@ config USB_MUSB_SUNXI
Say y here to enable support for the sunxi OTG / DRC USB controller Say y here to enable support for the sunxi OTG / DRC USB controller
used on almost all sunxi boards. used on almost all sunxi boards.
config USB_MUSB_UX500
bool "Enable ST-Ericsson Ux500 USB controller"
depends on DM_USB && DM_USB_GADGET && ARCH_U8500
default y
help
Say y to enable support for the MUSB OTG USB controller used in
ST-Ericsson Ux500. The driver supports either gadget or host mode
based on the selection of CONFIG_USB_MUSB_HOST.
config USB_MUSB_DISABLE_BULK_COMBINE_SPLIT config USB_MUSB_DISABLE_BULK_COMBINE_SPLIT
bool "Disable MUSB bulk split/combine" bool "Disable MUSB bulk split/combine"
default y default y
@ -85,7 +94,7 @@ endif
config USB_MUSB_PIO_ONLY config USB_MUSB_PIO_ONLY
bool "Disable DMA (always use PIO)" bool "Disable DMA (always use PIO)"
default y if USB_MUSB_AM35X || USB_MUSB_PIC32 || USB_MUSB_OMAP2PLUS || USB_MUSB_DSPS || USB_MUSB_SUNXI || USB_MUSB_MT85XX default y if USB_MUSB_AM35X || USB_MUSB_PIC32 || USB_MUSB_OMAP2PLUS || USB_MUSB_DSPS || USB_MUSB_SUNXI || USB_MUSB_MT85XX || USB_MUSB_UX500
help help
All data is copied between memory and FIFO by the CPU. All data is copied between memory and FIFO by the CPU.
DMA controllers are ignored. DMA controllers are ignored.

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o
obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
ccflags-y := $(call cc-option,-Wno-unused-variable) \ ccflags-y := $(call cc-option,-Wno-unused-variable) \
$(call cc-option,-Wno-unused-but-set-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \

View File

@ -1526,7 +1526,7 @@ static int __devinit musb_core_init(u16 musb_type, struct musb *musb)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \ #if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \
defined(CONFIG_ARCH_OMAP4) defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
static irqreturn_t generic_interrupt(int irq, void *__hci) static irqreturn_t generic_interrupt(int irq, void *__hci)
{ {

View File

@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (C) 2019 Stephan Gerhold */
#include <common.h>
#include <dm.h>
#include <generic-phy.h>
#include <dm/device_compat.h>
#include "musb_uboot.h"
static struct musb_hdrc_config ux500_musb_hdrc_config = {
.multipoint = true,
.dyn_fifo = true,
.num_eps = 16,
.ram_bits = 16,
};
struct ux500_glue {
struct musb_host_data mdata;
struct device dev;
struct phy phy;
bool enabled;
};
#define to_ux500_glue(d) container_of(d, struct ux500_glue, dev)
static int ux500_musb_enable(struct musb *musb)
{
struct ux500_glue *glue = to_ux500_glue(musb->controller);
int ret;
if (glue->enabled)
return 0;
ret = generic_phy_power_on(&glue->phy);
if (ret) {
printf("%s: failed to power on USB PHY\n", __func__);
return ret;
}
glue->enabled = true;
return 0;
}
static void ux500_musb_disable(struct musb *musb)
{
struct ux500_glue *glue = to_ux500_glue(musb->controller);
int ret;
if (!glue->enabled)
return;
ret = generic_phy_power_off(&glue->phy);
if (ret) {
printf("%s: failed to power off USB PHY\n", __func__);
return;
}
glue->enabled = false;
}
static int ux500_musb_init(struct musb *musb)
{
struct ux500_glue *glue = to_ux500_glue(musb->controller);
int ret;
ret = generic_phy_init(&glue->phy);
if (ret) {
printf("%s: failed to init USB PHY\n", __func__);
return ret;
}
return 0;
}
static int ux500_musb_exit(struct musb *musb)
{
struct ux500_glue *glue = to_ux500_glue(musb->controller);
int ret;
ret = generic_phy_exit(&glue->phy);
if (ret) {
printf("%s: failed to exit USB PHY\n", __func__);
return ret;
}
return 0;
}
static const struct musb_platform_ops ux500_musb_ops = {
.init = ux500_musb_init,
.exit = ux500_musb_exit,
.enable = ux500_musb_enable,
.disable = ux500_musb_disable,
};
int dm_usb_gadget_handle_interrupts(struct udevice *dev)
{
struct ux500_glue *glue = dev_get_priv(dev);
glue->mdata.host->isr(0, glue->mdata.host);
return 0;
}
static int ux500_musb_probe(struct udevice *dev)
{
#ifdef CONFIG_USB_MUSB_HOST
struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
#endif
struct ux500_glue *glue = dev_get_priv(dev);
struct musb_host_data *host = &glue->mdata;
struct musb_hdrc_platform_data pdata;
void *base = dev_read_addr_ptr(dev);
int ret;
if (!base)
return -EINVAL;
ret = generic_phy_get_by_name(dev, "usb", &glue->phy);
if (ret) {
dev_err(dev, "failed to get USB PHY: %d\n", ret);
return ret;
}
memset(&pdata, 0, sizeof(pdata));
pdata.platform_ops = &ux500_musb_ops;
pdata.config = &ux500_musb_hdrc_config;
#ifdef CONFIG_USB_MUSB_HOST
priv->desc_before_addr = true;
pdata.mode = MUSB_HOST;
host->host = musb_init_controller(&pdata, &glue->dev, base);
if (!host->host)
return -EIO;
return musb_lowlevel_init(host);
#else
pdata.mode = MUSB_PERIPHERAL;
host->host = musb_init_controller(&pdata, &glue->dev, base);
if (!host->host)
return -EIO;
return usb_add_gadget_udc(&glue->dev, &host->host->g);
#endif
}
static int ux500_musb_remove(struct udevice *dev)
{
struct ux500_glue *glue = dev_get_priv(dev);
struct musb_host_data *host = &glue->mdata;
usb_del_gadget_udc(&host->host->g);
musb_stop(host->host);
free(host->host);
host->host = NULL;
return 0;
}
static const struct udevice_id ux500_musb_ids[] = {
{ .compatible = "stericsson,db8500-musb" },
{ }
};
U_BOOT_DRIVER(ux500_musb) = {
.name = "ux500-musb",
#ifdef CONFIG_USB_MUSB_HOST
.id = UCLASS_USB,
#else
.id = UCLASS_USB_GADGET_GENERIC,
#endif
.of_match = ux500_musb_ids,
.probe = ux500_musb_probe,
.remove = ux500_musb_remove,
#ifdef CONFIG_USB_MUSB_HOST
.ops = &musb_usb_ops,
#endif
.plat_auto = sizeof(struct usb_plat),
.priv_auto = sizeof(struct ux500_glue),
};