diff options
author | Marek Vasut <marek.vasut@gmail.com> | 2017-10-15 15:01:29 +0200 |
---|---|---|
committer | Marek Vasut <marek.vasut+renesas@gmail.com> | 2017-11-26 02:22:36 +0100 |
commit | e1cc60c0d635408b18a31dcdc2002bba07da21a4 (patch) | |
tree | 39c1ba396243f6b233e08b17b94cb97ba31cf817 /drivers/usb/host/xhci-rcar.c | |
parent | 9829ce2ff25c659ca29cd15ab773312ac4b6cfc6 (diff) |
usb: xhci: Add Renesas R-Car xHCI driver
Add firmware V3, firmware loader and XHCI glue for the Renesas R-Car
Gen3 SoCs XHCI controller. Thus far only the R-Car Gen3 R8A7795 ES2.0+
and R8A7796 are supported.
Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Cc: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
Cc: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'drivers/usb/host/xhci-rcar.c')
-rw-r--r-- | drivers/usb/host/xhci-rcar.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c new file mode 100644 index 0000000000..d47c99644d --- /dev/null +++ b/drivers/usb/host/xhci-rcar.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> + * + * Renesas RCar USB HOST xHCI Controller + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <fdtdec.h> +#include <usb.h> +#include <wait_bit.h> + +#include "xhci.h" +#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("xhci-rcar", 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("xhci-rcar", 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_platdata(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) +{ + struct rcar_xhci_platdata *plat = dev_get_platdata(dev); + + clk_disable(&plat->clk); + clk_free(&plat->clk); + + return xhci_deregister(dev); +} + +static int xhci_rcar_ofdata_to_platdata(struct udevice *dev) +{ + struct rcar_xhci_platdata *plat = dev_get_platdata(dev); + + plat->hcd_base = devfdt_get_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,xhci-r8a7795" }, + { .compatible = "renesas,xhci-r8a7796" }, + { } +}; + +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, + .ofdata_to_platdata = xhci_rcar_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct rcar_xhci_platdata), + .priv_auto_alloc_size = sizeof(struct rcar_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; |