// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2017 Marek Vasut * * Renesas RCar USB HOST xHCI Controller */ #include #include #include #include #include #include #include #include #include #include #include #include "xhci-rcar-r8a779x_usb3_v3.h" /* Register Offset */ #define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ #define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ /* Register Settings */ /* FW Download Control & Status */ #define RCAR_USB3_DL_CTRL_ENABLE BIT(0) #define RCAR_USB3_DL_CTRL_FW_SUCCESS BIT(4) #define RCAR_USB3_DL_CTRL_FW_SET_DATA0 BIT(8) struct rcar_xhci_platdata { fdt_addr_t hcd_base; struct clk clk; }; /** * Contains pointers to register base addresses * for the usb controller. */ struct rcar_xhci { struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ struct usb_platdata usb_plat; struct xhci_hccr *hcd; }; static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data, const size_t fw_array_size) { void __iomem *regs = (void __iomem *)ctx->hcd; int i, ret; /* Download R-Car USB3.0 firmware */ setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE); for (i = 0; i < fw_array_size; i++) { writel(fw_data[i], regs + RCAR_USB3_FW_DATA0); setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SET_DATA0); ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, 10, false); if (ret) break; } clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE); ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SUCCESS, true, 10, false); return ret; } static int xhci_rcar_probe(struct udevice *dev) { struct rcar_xhci_platdata *plat = dev_get_plat(dev); struct rcar_xhci *ctx = dev_get_priv(dev); struct xhci_hcor *hcor; int len, ret; ret = clk_get_by_index(dev, 0, &plat->clk); if (ret < 0) { dev_err(dev, "Failed to get USB3 clock\n"); return ret; } ret = clk_enable(&plat->clk); if (ret) { dev_err(dev, "Failed to enable USB3 clock\n"); goto err_clk; } ctx->hcd = (struct xhci_hccr *)plat->hcd_base; len = HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase)); hcor = (struct xhci_hcor *)((uintptr_t)ctx->hcd + len); ret = xhci_rcar_download_fw(ctx, firmware_r8a779x_usb3_v3, ARRAY_SIZE(firmware_r8a779x_usb3_v3)); if (ret) { dev_err(dev, "Failed to download firmware\n"); goto err_fw; } ret = xhci_register(dev, ctx->hcd, hcor); if (ret) { dev_err(dev, "Failed to register xHCI\n"); goto err_fw; } return 0; err_fw: clk_disable(&plat->clk); err_clk: clk_free(&plat->clk); return ret; } static int xhci_rcar_deregister(struct udevice *dev) { int ret; struct rcar_xhci_platdata *plat = dev_get_plat(dev); ret = xhci_deregister(dev); clk_disable(&plat->clk); clk_free(&plat->clk); return ret; } static int xhci_rcar_of_to_plat(struct udevice *dev) { struct rcar_xhci_platdata *plat = dev_get_plat(dev); plat->hcd_base = dev_read_addr(dev); if (plat->hcd_base == FDT_ADDR_T_NONE) { debug("Can't get the XHCI register base address\n"); return -ENXIO; } return 0; } static const struct udevice_id xhci_rcar_ids[] = { { .compatible = "renesas,rcar-gen3-xhci" }, { .compatible = "renesas,xhci-r8a7795" }, { .compatible = "renesas,xhci-r8a7796" }, { .compatible = "renesas,xhci-r8a77965" }, { } }; U_BOOT_DRIVER(usb_xhci) = { .name = "xhci_rcar", .id = UCLASS_USB, .probe = xhci_rcar_probe, .remove = xhci_rcar_deregister, .ops = &xhci_usb_ops, .of_match = xhci_rcar_ids, .of_to_plat = xhci_rcar_of_to_plat, .plat_auto = sizeof(struct rcar_xhci_platdata), .priv_auto = sizeof(struct rcar_xhci), .flags = DM_FLAG_ALLOC_PRIV_DMA, };