1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> 4 * 5 * Renesas RCar USB HOST xHCI Controller 6 */ 7 8#include <common.h> 9#include <clk.h> 10#include <dm.h> 11#include <fdtdec.h> 12#include <log.h> 13#include <malloc.h> 14#include <usb.h> 15#include <wait_bit.h> 16#include <dm/device_compat.h> 17#include <linux/bitops.h> 18 19#include <usb/xhci.h> 20#include "xhci-rcar-r8a779x_usb3_v3.h" 21 22/* Register Offset */ 23#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ 24#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ 25 26/* Register Settings */ 27/* FW Download Control & Status */ 28#define RCAR_USB3_DL_CTRL_ENABLE BIT(0) 29#define RCAR_USB3_DL_CTRL_FW_SUCCESS BIT(4) 30#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 BIT(8) 31 32struct rcar_xhci_plat { 33 fdt_addr_t hcd_base; 34 struct clk clk; 35}; 36 37/** 38 * Contains pointers to register base addresses 39 * for the usb controller. 40 */ 41struct rcar_xhci { 42 struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ 43 struct usb_plat usb_plat; 44 struct xhci_hccr *hcd; 45}; 46 47static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data, 48 const size_t fw_array_size) 49{ 50 void __iomem *regs = (void __iomem *)ctx->hcd; 51 int i, ret; 52 53 /* Download R-Car USB3.0 firmware */ 54 setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE); 55 56 for (i = 0; i < fw_array_size; i++) { 57 writel(fw_data[i], regs + RCAR_USB3_FW_DATA0); 58 setbits_le32(regs + RCAR_USB3_DL_CTRL, 59 RCAR_USB3_DL_CTRL_FW_SET_DATA0); 60 61 ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, 62 RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, 63 10, false); 64 if (ret) 65 break; 66 } 67 68 clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE); 69 70 ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, 71 RCAR_USB3_DL_CTRL_FW_SUCCESS, true, 72 10, false); 73 74 return ret; 75} 76 77static int xhci_rcar_probe(struct udevice *dev) 78{ 79 struct rcar_xhci_plat *plat = dev_get_plat(dev); 80 struct rcar_xhci *ctx = dev_get_priv(dev); 81 struct xhci_hcor *hcor; 82 int len, ret; 83 84 ret = clk_get_by_index(dev, 0, &plat->clk); 85 if (ret < 0) { 86 dev_err(dev, "Failed to get USB3 clock\n"); 87 return ret; 88 } 89 90 ret = clk_enable(&plat->clk); 91 if (ret) { 92 dev_err(dev, "Failed to enable USB3 clock\n"); 93 return ret; 94 } 95 96 ctx->hcd = (struct xhci_hccr *)plat->hcd_base; 97 len = HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase)); 98 hcor = (struct xhci_hcor *)((uintptr_t)ctx->hcd + len); 99 100 ret = xhci_rcar_download_fw(ctx, firmware_r8a779x_usb3_v3, 101 ARRAY_SIZE(firmware_r8a779x_usb3_v3)); 102 if (ret) { 103 dev_err(dev, "Failed to download firmware\n"); 104 goto err_fw; 105 } 106 107 ret = xhci_register(dev, ctx->hcd, hcor); 108 if (ret) { 109 dev_err(dev, "Failed to register xHCI\n"); 110 goto err_fw; 111 } 112 113 return 0; 114 115err_fw: 116 clk_disable(&plat->clk); 117 return ret; 118} 119 120static int xhci_rcar_deregister(struct udevice *dev) 121{ 122 int ret; 123 struct rcar_xhci_plat *plat = dev_get_plat(dev); 124 125 ret = xhci_deregister(dev); 126 127 clk_disable(&plat->clk); 128 129 return ret; 130} 131 132static int xhci_rcar_of_to_plat(struct udevice *dev) 133{ 134 struct rcar_xhci_plat *plat = dev_get_plat(dev); 135 136 plat->hcd_base = dev_read_addr(dev); 137 if (plat->hcd_base == FDT_ADDR_T_NONE) { 138 debug("Can't get the XHCI register base address\n"); 139 return -ENXIO; 140 } 141 142 return 0; 143} 144 145static const struct udevice_id xhci_rcar_ids[] = { 146 { .compatible = "renesas,rcar-gen3-xhci" }, 147 { .compatible = "renesas,xhci-r8a7795" }, 148 { .compatible = "renesas,xhci-r8a7796" }, 149 { .compatible = "renesas,xhci-r8a77965" }, 150 { } 151}; 152 153U_BOOT_DRIVER(usb_xhci) = { 154 .name = "xhci_rcar", 155 .id = UCLASS_USB, 156 .probe = xhci_rcar_probe, 157 .remove = xhci_rcar_deregister, 158 .ops = &xhci_usb_ops, 159 .of_match = xhci_rcar_ids, 160 .of_to_plat = xhci_rcar_of_to_plat, 161 .plat_auto = sizeof(struct rcar_xhci_plat), 162 .priv_auto = sizeof(struct rcar_xhci), 163 .flags = DM_FLAG_ALLOC_PRIV_DMA, 164}; 165