// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Oleksandr Tymoshenko * Copyright (C) 2014 Marek Vasut * * Copied from u-Boot */ #include #include #include #include #include #include #include #include #include "dwc2.h" static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode) { struct dwc2 *dwc2 = ctx; int ret = -ENODEV; int oldmode = dwc2->dr_mode; dwc2->dr_mode = mode; if (mode == USB_DR_MODE_HOST || mode == USB_DR_MODE_OTG) { if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) ret = dwc2_register_host(dwc2); else dwc2_err(dwc2, "Host support not available\n"); } if (mode == USB_DR_MODE_PERIPHERAL || mode == USB_DR_MODE_OTG) { if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) ret = dwc2_gadget_init(dwc2); else dwc2_err(dwc2, "Peripheral support not available\n"); } if (ret) dwc2->dr_mode = oldmode; return ret; } static int dwc2_probe(struct device_d *dev) { struct resource *iores; struct dwc2 *dwc2; int ret; dwc2 = xzalloc(sizeof(*dwc2)); iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); dwc2->regs = IOMEM(iores->start); dwc2->dev = dev; dwc2->clk = clk_get(dev, "otg"); if (IS_ERR(dwc2->clk)) { ret = PTR_ERR(dwc2->clk); dev_err(dev, "Failed to get USB clock %d\n", ret); goto release_region; } ret = clk_enable(dwc2->clk); if (ret) goto clk_put; ret = device_reset_us(dev, 2); if (ret) goto clk_disable; dwc2->phy = phy_optional_get(dev, "usb2-phy"); if (IS_ERR(dwc2->phy)) { ret = PTR_ERR(dwc2->phy); goto clk_disable; } ret = phy_power_on(dwc2->phy); if (ret) goto clk_disable; ret = phy_init(dwc2->phy); if (ret) goto phy_power_off; ret = dwc2_check_core_version(dwc2); if (ret) goto error; /* * Reset before dwc2_get_hwparams() then it could get power-on real * reset value form registers. */ ret = dwc2_core_reset(dwc2); if (ret) goto error; /* Detect config values from hardware */ dwc2_get_hwparams(dwc2); ret = dwc2_get_dr_mode(dwc2); if (ret) goto error; dwc2_set_default_params(dwc2); dma_set_mask(dev, DMA_BIT_MASK(32)); dev->priv = dwc2; if (dwc2->dr_mode == USB_DR_MODE_OTG) ret = usb_register_otg_device(dwc2->dev, dwc2_set_mode, dwc2); else ret = dwc2_set_mode(dwc2, dwc2->dr_mode); if (ret) goto error; return 0; error: phy_exit(dwc2->phy); phy_power_off: phy_power_off(dwc2->phy); clk_disable: clk_disable(dwc2->clk); clk_put: clk_put(dwc2->clk); release_region: release_region(iores); return ret; } static void dwc2_remove(struct device_d *dev) { struct dwc2 *dwc2 = dev->priv; dwc2_host_uninit(dwc2); dwc2_gadget_uninit(dwc2); phy_exit(dwc2->phy); phy_power_off(dwc2->phy); } static const struct of_device_id dwc2_platform_dt_ids[] = { { .compatible = "brcm,bcm2835-usb", }, { .compatible = "brcm,bcm2708-usb", }, { .compatible = "snps,dwc2", }, { } }; static struct driver_d dwc2_driver = { .name = "dwc2", .probe = dwc2_probe, .remove = dwc2_remove, .of_compatible = DRV_OF_COMPAT(dwc2_platform_dt_ids), }; device_platform_driver(dwc2_driver);