From 845d9cf61c3a319fed0069c36f402e74a61ceb8c Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 8 Jul 2021 20:33:50 +0200 Subject: [PATCH] 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 Signed-off-by: Stephan Gerhold --- drivers/usb/musb-new/Kconfig | 11 +- drivers/usb/musb-new/Makefile | 1 + drivers/usb/musb-new/musb_core.c | 2 +- drivers/usb/musb-new/ux500.c | 179 +++++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/musb-new/ux500.c diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index fd6f4109b0..81ceea9740 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/drivers/usb/musb-new/Kconfig @@ -72,6 +72,15 @@ config USB_MUSB_SUNXI Say y here to enable support for the sunxi OTG / DRC USB controller 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 bool "Disable MUSB bulk split/combine" default y @@ -85,7 +94,7 @@ endif config USB_MUSB_PIO_ONLY 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 All data is copied between memory and FIFO by the CPU. DMA controllers are ignored. diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 6355eb12dd..396ff02654 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o +obj-$(CONFIG_USB_MUSB_UX500) += ux500.o ccflags-y := $(call cc-option,-Wno-unused-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \ diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c index 22811a5efb..18d9bc805f 100644 --- a/drivers/usb/musb-new/musb_core.c +++ b/drivers/usb/musb-new/musb_core.c @@ -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) || \ - defined(CONFIG_ARCH_OMAP4) + defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) static irqreturn_t generic_interrupt(int irq, void *__hci) { diff --git a/drivers/usb/musb-new/ux500.c b/drivers/usb/musb-new/ux500.c new file mode 100644 index 0000000000..57c7d5630d --- /dev/null +++ b/drivers/usb/musb-new/ux500.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2019 Stephan Gerhold */ + +#include +#include +#include +#include +#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), +}; -- 2.39.5