u-boot/drivers/usb/musb-new/ux500.c
Stephan Gerhold 845d9cf61c 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>
2021-07-14 16:48:16 -04:00

179 lines
3.7 KiB
C

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