[U-Boot] [PATCH 1/2] usb: musb-new: Add DA8xx MUSB Glue and enable Host mode
Marek Vasut
marex at denx.de
Tue Jul 2 21:41:29 UTC 2019
On 7/2/19 11:30 PM, Adam Ford wrote:
[...]
> @@ -0,0 +-2,466 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Texas Instruments da8xx "glue layer"
> + *
> + * Copyright (c) 2010, by Texas Instruments
> + *
> + * Based on the DA8xx "glue layer" code.
> + * Copyright (c) 2008-2009, MontaVista Software, Inc. <source at mvista.com>
> + *
> + * This file is part of the Inventra Controller Driver for Linux.
Shouldn't you grow copyright on this too ?
[...]
> + musb->int_usb &= ~MUSB_INTR_VBUSERROR;
> + //musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
> + //mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
Why is this commented out in such a crude c++ way ? Surely , checkpatch
complains about it ?
> + WARNING("VBUS error workaround (delay coming)\n");
> + } else if (drvvbus) {
> + MUSB_HST_MODE(musb);
> + //musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
> + //portstate(musb->port1_status |= USB_PORT_STAT_POWER);
> + //del_timer(&musb->dev_timer);
> + } else if (!(musb->int_usb & MUSB_INTR_BABBLE)) {
> + /*
> + * When babble condition happens, drvvbus interrupt
> + * is also generated. Ignore this drvvbus interrupt
> + * and let babble interrupt handler recovers the
> + * controller; otherwise, the host-mode flag is lost
> + * due to the MUSB_DEV_MODE() call below and babble
> + * recovery logic will not be called.
> + */
> + musb->is_active = 0;
> + MUSB_DEV_MODE(musb);
> + //musb->xceiv->otg->state = OTG_STATE_B_IDLE;
> + //portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
> + }
> +#if 0
#if 0? Delete the code then ?
> + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
> + drvvbus ? "on" : "off",
> + usb_otg_state_string(musb->xceiv->otg->state),
> + err ? " ERROR" : "",
> + devctl);
> +#endif
> + ret = IRQ_HANDLED;
> + }
> +
> + if (musb->int_tx || musb->int_rx || musb->int_usb)
> + ret |= musb_interrupt(musb);
> +eoi:
> + /* EOI needs to be written for the IRQ to be re-asserted. */
> + if (ret == IRQ_HANDLED || status)
> + musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
> +
> + spin_unlock_irqrestore(&musb->lock, flags);
> +
> + return ret;
> +}
> +
> +static void enable_vbus(void)
> +{
> + u32 value;
> +
> +#if 0
> + gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset");
> + gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on);
> +
> + /* configure GPIO bank4 pin 15 in output direction */
> + value = readl(&davinci_gpio_bank45->dir);
> + writel((value & (~DA8XX_USB_VBUS_GPIO)), &davinci_gpio_bank45->dir);
> +
> + /* set GPIO bank4 pin 15 high to drive VBUS */
> + value = readl(&davinci_gpio_bank45->set_data);
> + writel((value | DA8XX_USB_VBUS_GPIO), &davinci_gpio_bank45->set_data);
> +#endif
> +}
> +
> +/*
> + * Enable the usb0 phy. This initialization procedure is explained in
> + * the DA8xx USB user guide document.
> + */
> +static u8 phy_on(void)
> +{
> + u32 timeout;
> + u32 cfgchip2;
> +
> + cfgchip2 = readl(&davinci_syscfg_regs->cfgchip2);
> +
> + cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN |
> + CFGCHIP2_OTGMODE | CFGCHIP2_REFFREQ);
> + cfgchip2 |= CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN | CFGCHIP2_PHY_PLLON |
> + CFGCHIP2_REFFREQ_24MHZ;
> +
> + writel(cfgchip2, &davinci_syscfg_regs->cfgchip2);
> +
> + /* wait until the usb phy pll locks */
> + timeout = 0x3FFFFFF; //musb_cfg.timeout;
> + while (timeout--)
> + if (readl(&davinci_syscfg_regs->cfgchip2) & CFGCHIP2_PHYCLKGD)
> + return 1;
wait_for_bit ...
> + debug("Phy was not turned on\n");
> + /* USB phy was not turned on */
> + return 0;
> +}
> +
> +/*
> + * Disable the usb phy
> + */
> +static void phy_off(void)
> +{
> + u32 cfgchip2;
Implement proper PHY driver.
> + /*
> + * Power down the on-chip PHY.
> + */
> + cfgchip2 = readl(&davinci_syscfg_regs->cfgchip2);
clrsetbits_le32()
> + cfgchip2 &= ~CFGCHIP2_PHY_PLLON;
> + cfgchip2 |= CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN;
> + writel(cfgchip2, &davinci_syscfg_regs->cfgchip2);
> +}
> +
> +static void da8xx_musb_phy_power(struct udevice *dev, u8 on)
> +{
> + unsigned long start = get_timer(0);
start is unused
> + if (on)
> + phy_on();
> + else
> + phy_off();
> +}
> +
> +static int da8xx_musb_init(struct musb *musb)
> +{
> + u32 revision;
> + void __iomem *reg_base = musb->ctrl_base;
> +
> + /* enable psc for usb2.0 */
> + lpsc_on(33);
> +
> + /* enable usb vbus */
> + enable_vbus();
> +
> + /* reset the controller */
> + writel(0x1, &da8xx_usb_regs->control);
> + udelay(5000);
> +
> + /* start the on-chip usb phy and its pll */
> + if (phy_on() == 0) {
> + return -1;
Use errno.h error codes
> + }
> +
> + /* Returns zero if e.g. not clocked */
> + revision = readl(&da8xx_usb_regs->revision);
> + if (revision == 0) {
> + return -1;
> + }
> +
> + /* Disable all interrupts */
> + writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK |
> + DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_set);
> +
> + musb->mregs += DA8XX_MENTOR_CORE_OFFSET;
> +
> + /* NOTE: IRQs are in mixed mode, not bypass to pure MUSB */
> + debug("DA8xx OTG revision %08x, control %02x\n", revision,
> + musb_readb(reg_base, DA8XX_USB_CTRL_REG));
> +
> + musb->isr = da8xx_musb_interrupt;
> + return 0;
> +}
> +
> +static int da8xx_musb_exit(struct musb *musb)
> +{
> + /* Turn of the phy */
> + phy_off();
> +
> + /* flush any interrupts */
> + writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK |
> + DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_clr);
> + writel(0, &da8xx_usb_regs->eoi);
> +
> + return 0;
> +}
> +
> +/**
> + * da8xx_musb_enable - enable interrupts
> + */
> +static int da8xx_musb_enable(struct musb *musb)
> +{
> + void __iomem *reg_base = musb->ctrl_base;
> + u32 mask;
> +
> + /* Workaround: setup IRQs through both register sets. */
> + mask = ((musb->epmask & DA8XX_USB_TX_EP_MASK) << DA8XX_INTR_TX_SHIFT) |
> + ((musb->epmask & DA8XX_USB_RX_EP_MASK) << DA8XX_INTR_RX_SHIFT) |
> + DA8XX_INTR_USB_MASK;
> + musb_writel(reg_base, DA8XX_USB_INTR_MASK_SET_REG, mask);
> +
> + /* Force the DRVVBUS IRQ so we can start polling for ID change. */
> + musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG,
> + DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT);
> +
> + return 0;
> +}
> +
> +/**
> + * da8xx_musb_disable - disable HDRC and flush interrupts
> + */
> +static void da8xx_musb_disable(struct musb *musb)
> +{
> + void __iomem *reg_base = musb->ctrl_base;
> +
> + musb_writel(reg_base, DA8XX_USB_INTR_MASK_CLEAR_REG,
> + DA8XX_INTR_USB_MASK |
> + DA8XX_INTR_TX_MASK | DA8XX_INTR_RX_MASK);
> + musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
> +}
> +
> +void da8xx_musb_reset(struct udevice *dev)
> +{
> + void *reg_base = dev_read_addr_ptr(dev);
> + /* Reset the controller */
> + musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
> +}
> +
> +void da8xx_musb_clear_irq(struct udevice *dev)
> +{
> + /* flush any interrupts */
> + writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK |
> + DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_clr);
> + writel(0, &da8xx_usb_regs->eoi);
> +}
> +
> +const struct musb_platform_ops da8xx_ops = {
> + .init = da8xx_musb_init,
> + .exit = da8xx_musb_exit,
> + .enable = da8xx_musb_enable,
> + .disable = da8xx_musb_disable,
> +};
> +
> +#if CONFIG_IS_ENABLED(DM_USB)
DM should always be enabled for new code, any reason for adding non-DM
stuff ?
> +struct da8xx_musb_platdata {
> + void *base;
> + void *ctrl_mod_base;
> + struct musb_hdrc_platform_data plat;
> + struct musb_hdrc_config musb_config;
> + struct omap_musb_board_data otg_board_data;
> +};
> +
> +static int da8xx_musb_ofdata_to_platdata(struct udevice *dev)
> +{
> + struct da8xx_musb_platdata *platdata = dev_get_platdata(dev);
> + const void *fdt = gd->fdt_blob;
> + int node = dev_of_offset(dev);
> +
> + platdata->base = (void *)dev_read_addr_ptr(dev);
> + platdata->musb_config.multipoint = 1;
> + platdata->musb_config.dyn_fifo = 1;
> + platdata->musb_config.num_eps = 5;
> + platdata->musb_config.ram_bits = 10;
> + platdata->plat.power = fdtdec_get_int(fdt, node,
> + "power", -1);
Set default to 50 ^ and drop this code below v
> + if (platdata->plat.power < 0) {
> + platdata->plat.power = 50; /* If power isn't specified, assume 50mA */
> + }
> +
> + platdata->otg_board_data.interface_type = MUSB_INTERFACE_UTMI;
> +
> +#if 0 /* In a perfect world, mode would be set to OTG, mode 3 from DT */
Drop dead code
> + platdata->plat.mode = fdtdec_get_int(fdt, node,
> + "mode", -1);
> + if (platdata->plat.mode < 0) {
> + pr_err("MUSB mode DT entry missing\n");
> + return -ENOENT;
> + }
> +#else /* MUSB_OTG, it doesn't work */
Why are you submitting stuff that does not work ?
> +#ifdef CONFIG_USB_MUSB_HOST /* Host seems to be the only option that works */
> + platdata->plat.mode = MUSB_HOST;
> +#else /* For that matter, MUSB_PERIPHERAL doesn't either */
> + platdata->plat.mode = MUSB_PERIPHERAL;
> +#endif
> +#endif
> + platdata->otg_board_data.dev = dev;
> + platdata->plat.config = &platdata->musb_config;
> + platdata->plat.platform_ops = &da8xx_ops;
> + platdata->plat.board_data = &platdata->otg_board_data;
> + platdata->otg_board_data.set_phy_power = da8xx_musb_phy_power;
> + platdata->otg_board_data.clear_irq = da8xx_musb_clear_irq;
> + platdata->otg_board_data.reset = da8xx_musb_reset;
> + return 0;
> +}
> +
> +static int da8xx_musb_probe(struct udevice *dev)
> +{
> +#ifdef CONFIG_USB_MUSB_HOST
> + struct musb_host_data *host = dev_get_priv(dev);
> +#endif
> + struct da8xx_musb_platdata *platdata = dev_get_platdata(dev);
> + struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
> + struct omap_musb_board_data *otg_board_data;
> + int ret;
> + void *base = dev_read_addr_ptr(dev);
> +
> + priv->desc_before_addr = true;
> +
> + otg_board_data = &platdata->otg_board_data;
> +
> +#ifdef CONFIG_USB_MUSB_HOST
> + host->host = musb_init_controller(&platdata->plat,
> + (struct device *)otg_board_data,
> + platdata->base);
> + if (!host->host) {
> + return -EIO;
> + }
> +
> + ret = musb_lowlevel_init(host);
> +#else
> + ret = musb_register(&platdata->plat,
> + (struct device *)otg_board_data,
> + platdata->base);
> +#endif
> + return ret;
> +}
> +
> +static int da8xx_musb_remove(struct udevice *dev)
> +{
> + struct musb_host_data *host = dev_get_priv(dev);
> +
> + musb_stop(host->host);
> +
> + return 0;
> +}
> +
> +static const struct udevice_id da8xx_musb_ids[] = {
> + { .compatible = "ti,da830-musb" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(da8xx_musb) = {
> + .name = "da8xx-musb",
> +#ifdef CONFIG_USB_MUSB_HOST
> + .id = UCLASS_USB,
> +#else
> + .id = UCLASS_USB_GADGET_GENERIC,
> +#endif
> + .of_match = da8xx_musb_ids,
> + .ofdata_to_platdata = da8xx_musb_ofdata_to_platdata,
> + .probe = da8xx_musb_probe,
> + .remove = da8xx_musb_remove,
> +#ifdef CONFIG_USB_MUSB_HOST
> + .ops = &musb_usb_ops,
> +#endif
> + .platdata_auto_alloc_size = sizeof(struct da8xx_musb_platdata),
> + .priv_auto_alloc_size = sizeof(struct musb_host_data),
> +};
> +
> +#endif /* CONFIG_IS_ENABLED(DM_USB) */
> +
>
--
Best regards,
Marek Vasut
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-56 Email: marex at denx.de
More information about the U-Boot
mailing list