// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Alexey Brodkin */ #include #include #include #include #include #include #include #include #include #include #include "ehci.h" #include /* * Even though here we don't explicitly use "struct ehci_ctrl" * ehci_register() expects it to be the first thing that resides in * device's private data. */ struct generic_ehci { struct ehci_ctrl ctrl; struct clk_bulk clocks; struct reset_ctl_bulk resets; struct phy phy; struct udevice *vbus_supply; }; static int ehci_enable_vbus_supply(struct udevice *dev) { struct generic_ehci *priv = dev_get_priv(dev); int ret; ret = device_get_supply_regulator(dev, "vbus-supply", &priv->vbus_supply); if (ret && ret != -ENOENT) return ret; if (priv->vbus_supply) { ret = regulator_set_enable(priv->vbus_supply, true); if (ret) { dev_err(dev, "Error enabling VBUS supply (ret=%d)\n", ret); return ret; } } else { dev_dbg(dev, "No vbus supply\n"); } return 0; } static int ehci_disable_vbus_supply(struct generic_ehci *priv) { if (priv->vbus_supply) return regulator_set_enable(priv->vbus_supply, false); else return 0; } static int ehci_usb_probe(struct udevice *dev) { struct generic_ehci *priv = dev_get_priv(dev); struct ehci_hccr *hccr; struct ehci_hcor *hcor; int err, ret; err = 0; ret = clk_get_bulk(dev, &priv->clocks); if (ret && ret != -ENOENT) { dev_err(dev, "Failed to get clocks (ret=%d)\n", ret); return ret; } err = clk_enable_bulk(&priv->clocks); if (err) { dev_err(dev, "Failed to enable clocks (err=%d)\n", err); goto clk_err; } err = reset_get_bulk(dev, &priv->resets); if (err && err != -ENOENT) { dev_err(dev, "Failed to get resets (err=%d)\n", err); goto clk_err; } err = reset_deassert_bulk(&priv->resets); if (err) { dev_err(dev, "Failed to get deassert resets (err=%d)\n", err); goto reset_err; } err = ehci_enable_vbus_supply(dev); if (err) goto reset_err; err = ehci_setup_phy(dev, &priv->phy, 0); if (err) goto regulator_err; hccr = map_physmem(dev_read_addr(dev), 0x100, MAP_NOCACHE); hcor = (struct ehci_hcor *)((uintptr_t)hccr + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); err = ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); if (err) goto phy_err; return 0; phy_err: ret = ehci_shutdown_phy(dev, &priv->phy); if (ret) dev_err(dev, "failed to shutdown usb phy (ret=%d)\n", ret); regulator_err: ret = ehci_disable_vbus_supply(priv); if (ret) dev_err(dev, "failed to disable VBUS supply (ret=%d)\n", ret); reset_err: ret = reset_release_bulk(&priv->resets); if (ret) dev_err(dev, "failed to release resets (ret=%d)\n", ret); clk_err: ret = clk_release_bulk(&priv->clocks); if (ret) dev_err(dev, "failed to release clocks (ret=%d)\n", ret); return err; } static int ehci_usb_remove(struct udevice *dev) { struct generic_ehci *priv = dev_get_priv(dev); int ret; ret = ehci_deregister(dev); if (ret) return ret; ret = ehci_shutdown_phy(dev, &priv->phy); if (ret) return ret; ret = ehci_disable_vbus_supply(priv); if (ret) return ret; ret = reset_release_bulk(&priv->resets); if (ret) return ret; return clk_release_bulk(&priv->clocks); } static const struct udevice_id ehci_usb_ids[] = { { .compatible = "generic-ehci" }, { } }; U_BOOT_DRIVER(ehci_generic) = { .name = "ehci_generic", .id = UCLASS_USB, .of_match = ehci_usb_ids, .probe = ehci_usb_probe, .remove = ehci_usb_remove, .ops = &ehci_usb_ops, .priv_auto = sizeof(struct generic_ehci), .flags = DM_FLAG_ALLOC_PRIV_DMA, };