| /* |
| * Copyright 2015 Freescale Semiconductor, Inc. |
| * |
| * DWC3 controller driver |
| * |
| * Author: Ramneek Mehresh<ramneek.mehresh@freescale.com> |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| |
| * Copyright (c) 2014, 2015 The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <usb.h> |
| #include <linux/compat.h> |
| #include <asm-generic/errno.h> |
| #include <linux/usb/dwc3.h> |
| #include "xhci.h" |
| |
| /* Declare global data pointer */ |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| struct ipq_xhci_platdata { |
| fdt_addr_t hcd_base; |
| unsigned int rst_ctrl; |
| unsigned int hs_only; |
| }; |
| |
| struct ipq_xhci { |
| struct ipq_xhci_platdata usb_plat; |
| struct xhci_ctrl ctrl; |
| struct udevice* dev; |
| struct xhci_hccr *hcd; |
| struct dwc3 *dwc3_reg; |
| }; |
| |
| void ipq_reset_usb_phy(void *data) |
| { |
| unsigned int gcc_rst_ctrl; |
| struct ipq_xhci_platdata *platdata; |
| struct ipq_xhci *ipq = (struct ipq_xhci *)data; |
| |
| platdata = dev_get_platdata(ipq->dev); |
| if (platdata == NULL) { |
| printf("Error: %s Failed\n", __func__); |
| return; |
| } |
| |
| gcc_rst_ctrl = platdata->rst_ctrl; |
| |
| if (gcc_rst_ctrl) { |
| /* assert HS PHY POR reset */ |
| setbits_le32(gcc_rst_ctrl, 0x10); |
| mdelay(10); |
| |
| /* assert HS PHY SRIF reset */ |
| setbits_le32(gcc_rst_ctrl, 0x4); |
| mdelay(10); |
| |
| /* deassert HS PHY SRIF reset and program HS PHY registers */ |
| clrbits_le32(gcc_rst_ctrl, 0x4); |
| mdelay(10); |
| |
| /* de-assert USB3 HS PHY POR reset */ |
| clrbits_le32(gcc_rst_ctrl, 0x10); |
| mdelay(10); |
| |
| if (!platdata->hs_only) { |
| /* assert SS PHY POR reset */ |
| setbits_le32(gcc_rst_ctrl, 0x20); |
| mdelay(10); |
| |
| /* deassert SS PHY POR reset */ |
| clrbits_le32(gcc_rst_ctrl, 0x20); |
| } |
| } |
| } |
| |
| static int ipq_xhci_core_init(struct ipq_xhci *ipq) |
| { |
| int ret = 0; |
| |
| ipq_reset_usb_phy((void *)ipq); |
| |
| ret = dwc3_core_init(ipq->dwc3_reg); |
| if (ret) { |
| debug("%s:failed to initialize core\n", __func__); |
| return ret; |
| } |
| |
| /* We are hard-coding DWC3 core to Host Mode */ |
| dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); |
| |
| return ret; |
| } |
| |
| static void ipq_xhci_core_exit(struct ipq_xhci *ipq) |
| { |
| |
| } |
| |
| static int xhci_usb_remove(struct udevice *dev) |
| { |
| int ret; |
| ret = xhci_deregister(dev); |
| |
| if (ret != 0) { |
| debug("%s:xhci deregistration failed\n", __func__); |
| return ret; |
| } |
| |
| ipq_xhci_core_exit(dev_get_priv(dev)); |
| |
| return 0; |
| } |
| |
| static int xhci_usb_probe(struct udevice *dev) |
| { |
| struct ipq_xhci *context; |
| struct ipq_xhci_platdata *platdata; |
| struct xhci_hcor *hcor; |
| int ret; |
| |
| platdata = dev_get_platdata(dev); |
| if (platdata == NULL) { |
| printf("Error: %s Failed\n", __func__); |
| return -ENODEV; |
| } |
| |
| context = dev_get_priv(dev); |
| if (context == NULL) { |
| printf("Error: %s Failed\n", __func__); |
| return -ENODEV; |
| } |
| |
| context->hcd = (struct xhci_hccr *)platdata->hcd_base; |
| context->dev = dev; |
| context->dwc3_reg = (struct dwc3 *)((char *)(context->hcd) + DWC3_REG_OFFSET); |
| hcor = (struct xhci_hcor *)((uint32_t)context->hcd + |
| HC_LENGTH(xhci_readl(&context->hcd->cr_capbase))); |
| |
| ret = ipq_xhci_core_init(context); |
| |
| if (ret) { |
| puts("Error initializing the XHCI controller\n"); |
| return -EINVAL; |
| } |
| |
| return xhci_register(dev, context->hcd, hcor); |
| } |
| |
| static int xhci_ofdata_to_platdata(struct udevice *dev) |
| { |
| struct ipq_xhci_platdata *platdata; |
| const void *blob = gd->fdt_blob; |
| |
| platdata = dev_get_platdata(dev); |
| if (platdata == NULL) { |
| printf("Error: %s Failed\n", __func__); |
| return -ENODEV; |
| } |
| |
| platdata->hcd_base = dev_get_addr(dev); |
| if (platdata->hcd_base == FDT_ADDR_T_NONE) { |
| debug("Error getting DWC3 base address\n"); |
| return -ENXIO; |
| } |
| |
| platdata->rst_ctrl = fdtdec_get_int(blob, dev->of_offset, "rst_ctrl", 0); |
| platdata->hs_only = fdtdec_get_int(blob, dev->of_offset, "hs_only", 0); |
| |
| return 0; |
| } |
| |
| static const struct udevice_id xhci_match_ids[] = { |
| { .compatible = "qca,dwc3-ipq" }, |
| {} |
| }; |
| |
| U_BOOT_DRIVER(usb_xhci) = { |
| .name = "xhci_ipq", |
| .id = UCLASS_USB, |
| .of_match = xhci_match_ids, |
| .ofdata_to_platdata = xhci_ofdata_to_platdata, |
| .probe = xhci_usb_probe, |
| .remove = xhci_usb_remove, |
| .ops = &xhci_usb_ops, |
| .platdata_auto_alloc_size = sizeof(struct ipq_xhci_platdata), |
| .priv_auto_alloc_size = sizeof(struct ipq_xhci), |
| .flags = DM_FLAG_ALLOC_PRIV_DMA, |
| }; |