usb: musb-new: Add support for Analog Devices SC5xx SoCs

This adds support for the MUSB-based USB controller found in the
Analog Devices SC57x and SC58x SoCs.

Co-developed-by: Greg Malysa <malysagreg@gmail.com>
Signed-off-by: Greg Malysa <malysagreg@gmail.com>
Co-developed-by: Ian Roberts <ian.roberts@timesys.com>
Signed-off-by: Ian Roberts <ian.roberts@timesys.com>
Signed-off-by: Vasileios Bimpikas <vasileios.bimpikas@analog.com>
Signed-off-by: Utsav Agarwal <utsav.agarwal@analog.com>
Signed-off-by: Arturs Artamonovs <arturs.artamonovs@analog.com>
Signed-off-by: Oliver Gaskell <Oliver.Gaskell@analog.com>
Signed-off-by: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
This commit is contained in:
Nathan Barrett-Morrison 2025-02-26 12:30:27 -05:00 committed by Tom Rini
parent 446179627f
commit 2f6a86a612
4 changed files with 211 additions and 0 deletions

View file

@ -637,6 +637,7 @@ F: drivers/gpio/gpio-adi-adsp.c
F: drivers/pinctrl/pinctrl-adi-adsp.c
F: drivers/serial/serial_adi_uart4.c
F: drivers/timer/adi_sc5xx_timer.c
F: drivers/usb/musb-new/sc5xx.c
F: include/configs/sc5*
F: include/dt-bindings/pinctrl/adi-adsp.h
F: include/env/adi/

View file

@ -22,6 +22,13 @@ config USB_MUSB_GADGET
Enables the MUSB USB dual-role controller in gadget mode.
if USB_MUSB_HOST || USB_MUSB_GADGET
config USB_MUSB_SC5XX
bool "Analog Devices MUSB support"
depends on (SC57X || SC58X)
help
Say y here to enable support for the USB controller on
ADI SC57X/SC58X processors.
config USB_MUSB_DA8XX
bool "Enable DA8xx MUSB Controller"
depends on ARCH_DAVINCI

View file

@ -14,6 +14,7 @@ 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
obj-$(CONFIG_USB_MUSB_SC5XX) += sc5xx.o
ccflags-y := $(call cc-option,-Wno-unused-variable) \
$(call cc-option,-Wno-unused-but-set-variable) \

View file

@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* ADI SC5XX MUSB "glue layer"
*
* Written and/or maintained by Timesys Corporation
*
* Loosely ported from Linux driver:
* Author: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
*
*/
#include <dm.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/usb/musb.h>
#include "linux-compat.h"
#include "musb_core.h"
#include "musb_uboot.h"
#define MUSB_SOFTRST 0x7f
#define MUSB_SOFTRST_NRST BIT(0)
#define MUSB_SOFTRST_NRSTX BIT(1)
#define REG_USB_VBUS_CTL 0x380
#define REG_USB_ID_CTL 0x382
#define REG_USB_PHY_CTL 0x394
#define REG_USB_PLL_OSC 0x398
#define REG_USB_UTMI_CTL 0x39c
/* controller data */
struct sc5xx_musb_data {
struct musb_host_data mdata;
struct device dev;
};
#define to_sc5xx_musb_data(d) \
container_of(d, struct sc5xx_musb_data, dev)
static void sc5xx_musb_disable(struct musb *musb)
{
/* no way to shut the controller */
}
static int sc5xx_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 sc5xx_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
irqreturn_t ret = IRQ_NONE;
u8 devctl;
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
if (musb->int_usb & MUSB_INTR_VBUSERROR) {
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
devctl = musb_readw(musb->mregs, MUSB_DEVCTL);
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
}
if (musb->int_usb || musb->int_tx || musb->int_rx) {
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
ret = musb_interrupt(musb);
}
if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))
musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
return ret;
}
static int sc5xx_musb_set_mode(struct musb *musb, u8 mode)
{
struct device *dev = musb->controller;
struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(dev);
switch (mode) {
case MUSB_HOST:
musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x1);
break;
case MUSB_PERIPHERAL:
musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x3);
break;
case MUSB_OTG:
musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x0);
break;
default:
dev_err(dev, "unsupported mode %d\n", mode);
return -EINVAL;
}
return 0;
}
static int sc5xx_musb_init(struct musb *musb)
{
struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(musb->controller);
musb->isr = sc5xx_interrupt;
musb_writel(musb->mregs, REG_USB_PLL_OSC, 20 << 1);
musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
musb_writeb(musb->mregs, REG_USB_PHY_CTL, 0x80);
musb_writel(musb->mregs, REG_USB_UTMI_CTL,
0x40 | musb_readl(musb->mregs, REG_USB_UTMI_CTL));
return 0;
}
const struct musb_platform_ops sc5xx_musb_ops = {
.init = sc5xx_musb_init,
.set_mode = sc5xx_musb_set_mode,
.disable = sc5xx_musb_disable,
.enable = sc5xx_musb_enable,
};
static struct musb_hdrc_config sc5xx_musb_config = {
.multipoint = 1,
.dyn_fifo = 1,
.num_eps = 16,
.ram_bits = 12,
};
/* has one MUSB controller which can be host or gadget */
static struct musb_hdrc_platform_data sc5xx_musb_plat = {
.mode = MUSB_HOST,
.config = &sc5xx_musb_config,
.power = 100,
.platform_ops = &sc5xx_musb_ops,
};
static int musb_usb_probe(struct udevice *dev)
{
struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
struct sc5xx_musb_data *pdata = dev_get_priv(dev);
struct musb_host_data *mdata = &pdata->mdata;
void __iomem *mregs;
int ret;
priv->desc_before_addr = true;
mregs = dev_remap_addr(dev);
if (!mregs)
return -EINVAL;
/* init controller */
if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) {
mdata->host = musb_init_controller(&sc5xx_musb_plat,
&pdata->dev, mregs);
if (!mdata->host)
return -EIO;
ret = musb_lowlevel_init(mdata);
} else {
sc5xx_musb_plat.mode = MUSB_PERIPHERAL;
mdata->host = musb_register(&sc5xx_musb_plat, &pdata->dev, mregs);
if (!mdata->host)
return -EIO;
}
return ret;
}
static int musb_usb_remove(struct udevice *dev)
{
struct sc5xx_musb_data *pdata = dev_get_priv(dev);
musb_stop(pdata->mdata.host);
return 0;
}
static const struct udevice_id sc5xx_musb_ids[] = {
{ .compatible = "adi,sc5xx-musb" },
{ }
};
U_BOOT_DRIVER(usb_musb) = {
.name = "sc5xx-musb",
.id = UCLASS_USB,
.of_match = sc5xx_musb_ids,
.probe = musb_usb_probe,
.remove = musb_usb_remove,
#ifdef CONFIG_USB_MUSB_HOST
.ops = &musb_usb_ops,
#endif
.plat_auto = sizeof(struct usb_plat),
.priv_auto = sizeof(struct sc5xx_musb_data),
};