瀏覽代碼

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>
Stephan Gerhold 2 年之前
父節點
當前提交
845d9cf61c
共有 4 個文件被更改,包括 191 次插入2 次删除
  1. 10 1
      drivers/usb/musb-new/Kconfig
  2. 1 0
      drivers/usb/musb-new/Makefile
  3. 1 1
      drivers/usb/musb-new/musb_core.c
  4. 179 0
      drivers/usb/musb-new/ux500.c

+ 10 - 1
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.

+ 1 - 0
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) \

+ 1 - 1
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)
 {

+ 179 - 0
drivers/usb/musb-new/ux500.c

@@ -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),
+};