Merge tag 'ti-v2020.07-rc3' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
[oweals/u-boot.git] / drivers / usb / host / ohci-da8xx.c
index f0ccb83f7acf7588c751fd78d3f743c8b6a36e00..692018243c8e8bfa1dcf0079a57c09b17d91f4fe 100644 (file)
@@ -1,21 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2012 Sughosh Ganu <urwithsughosh@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #include <common.h>
-
+#include <malloc.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/ofnode.h>
+#include <generic-phy.h>
+#include <reset.h>
+#include "ohci.h"
 #include <asm/arch/da8xx-usb.h>
 
+struct da8xx_ohci {
+       ohci_t ohci;
+       struct clk *clocks;     /* clock list */
+       struct phy phy;
+       int clock_count;        /* number of clock in clock list */
+};
+
+static int usb_phy_on(void)
+{
+       unsigned long timeout;
+
+       clrsetbits_le32(&davinci_syscfg_regs->cfgchip2,
+                       (CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN |
+                       CFGCHIP2_OTGPWRDN | CFGCHIP2_OTGMODE |
+                       CFGCHIP2_REFFREQ | CFGCHIP2_USB1PHYCLKMUX),
+                       (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN |
+                       CFGCHIP2_PHY_PLLON | CFGCHIP2_REFFREQ_24MHZ |
+                       CFGCHIP2_USB2PHYCLKMUX | CFGCHIP2_USB1SUSPENDM));
+
+       /* wait until the usb phy pll locks */
+       timeout = get_timer(0);
+       while (get_timer(timeout) < 10) {
+               if (readl(&davinci_syscfg_regs->cfgchip2) & CFGCHIP2_PHYCLKGD)
+                       return 1;
+       }
+
+       /* USB phy was not turned on */
+       return 0;
+}
+
+static void usb_phy_off(void)
+{
+       /* Power down the on-chip PHY. */
+       clrsetbits_le32(&davinci_syscfg_regs->cfgchip2,
+                       CFGCHIP2_PHY_PLLON | CFGCHIP2_USB1SUSPENDM,
+                       CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN |
+                       CFGCHIP2_RESET);
+}
+
 int usb_cpu_init(void)
 {
        /* enable psc for usb2.0 */
@@ -46,3 +85,95 @@ int usb_cpu_init_fail(void)
 {
        return usb_cpu_stop();
 }
+
+#if CONFIG_IS_ENABLED(DM_USB)
+static int ohci_da8xx_probe(struct udevice *dev)
+{
+       struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev);
+       struct da8xx_ohci *priv = dev_get_priv(dev);
+       int i, err, ret, clock_nb;
+
+       err = 0;
+       priv->clock_count = 0;
+       clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells");
+
+       if (clock_nb < 0)
+               return clock_nb;
+
+       if (clock_nb > 0) {
+               priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk),
+                                           GFP_KERNEL);
+               if (!priv->clocks)
+                       return -ENOMEM;
+
+               for (i = 0; i < clock_nb; i++) {
+                       err = clk_get_by_index(dev, i, &priv->clocks[i]);
+                       if (err < 0)
+                               break;
+
+                       err = clk_enable(&priv->clocks[i]);
+                       if (err) {
+                               dev_err(dev, "failed to enable clock %d\n", i);
+                               clk_free(&priv->clocks[i]);
+                               goto clk_err;
+                       }
+                       priv->clock_count++;
+               }
+       }
+
+       err = usb_cpu_init();
+
+       if (err)
+               goto clk_err;
+
+       err = ohci_register(dev, regs);
+       if (err)
+               goto phy_err;
+
+       return 0;
+
+phy_err:
+       ret = usb_cpu_stop();
+       if (ret)
+               dev_err(dev, "failed to shutdown usb phy\n");
+
+clk_err:
+       ret = clk_release_all(priv->clocks, priv->clock_count);
+       if (ret)
+               dev_err(dev, "failed to disable all clocks\n");
+
+       return err;
+}
+
+static int ohci_da8xx_remove(struct udevice *dev)
+{
+       struct da8xx_ohci *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = ohci_deregister(dev);
+       if (ret)
+               return ret;
+
+       ret = usb_cpu_stop();
+       if (ret)
+               return ret;
+
+       return clk_release_all(priv->clocks, priv->clock_count);
+}
+
+static const struct udevice_id da8xx_ohci_ids[] = {
+       { .compatible = "ti,da830-ohci" },
+       { }
+};
+
+U_BOOT_DRIVER(ohci_generic) = {
+       .name   = "ohci-da8xx",
+       .id     = UCLASS_USB,
+       .of_match = da8xx_ohci_ids,
+       .probe = ohci_da8xx_probe,
+       .remove = ohci_da8xx_remove,
+       .ops    = &ohci_usb_ops,
+       .priv_auto_alloc_size = sizeof(struct da8xx_ohci),
+       .flags  = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_OS_PREPARE,
+};
+#endif