1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Cadence USBSS and USBSSP DRD Driver - host side 4 * 5 * Copyright (C) 2018-2019 Cadence Design Systems. 6 * Copyright (C) 2017-2018 NXP 7 * 8 * Authors: Peter Chen <peter.chen@nxp.com> 9 * Pawel Laszczak <pawell@cadence.com> 10 */ 11 12 #include <linux/platform_device.h> 13 #include <linux/slab.h> 14 #include "core.h" 15 #include "drd.h" 16 #include "host-export.h" 17 #include <linux/usb/hcd.h> 18 #include "../host/xhci.h" 19 #include "../host/xhci-plat.h" 20 21 #define XECP_PORT_CAP_REG 0x8000 22 #define XECP_AUX_CTRL_REG1 0x8120 23 24 #define CFG_RXDET_P3_EN BIT(15) 25 #define LPM_2_STB_SWITCH_EN BIT(25) 26 27 static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd); 28 29 static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { 30 .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI, 31 .suspend_quirk = xhci_cdns3_suspend_quirk, 32 }; 33 34 static int __cdns_host_init(struct cdns *cdns) 35 { 36 struct platform_device *xhci; 37 int ret; 38 struct usb_hcd *hcd; 39 40 cdns_drd_host_on(cdns); 41 42 xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); 43 if (!xhci) { 44 dev_err(cdns->dev, "couldn't allocate xHCI device\n"); 45 return -ENOMEM; 46 } 47 48 xhci->dev.parent = cdns->dev; 49 cdns->host_dev = xhci; 50 51 ret = platform_device_add_resources(xhci, cdns->xhci_res, 52 CDNS_XHCI_RESOURCES_NUM); 53 if (ret) { 54 dev_err(cdns->dev, "couldn't add resources to xHCI device\n"); 55 goto err1; 56 } 57 58 cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci, 59 sizeof(struct xhci_plat_priv), GFP_KERNEL); 60 if (!cdns->xhci_plat_data) { 61 ret = -ENOMEM; 62 goto err1; 63 } 64 65 if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) 66 cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; 67 68 ret = platform_device_add_data(xhci, cdns->xhci_plat_data, 69 sizeof(struct xhci_plat_priv)); 70 if (ret) 71 goto free_memory; 72 73 ret = platform_device_add(xhci); 74 if (ret) { 75 dev_err(cdns->dev, "failed to register xHCI device\n"); 76 goto free_memory; 77 } 78 79 /* Glue needs to access xHCI region register for Power management */ 80 hcd = platform_get_drvdata(xhci); 81 if (hcd) 82 cdns->xhci_regs = hcd->regs; 83 84 return 0; 85 86 free_memory: 87 kfree(cdns->xhci_plat_data); 88 err1: 89 platform_device_put(xhci); 90 return ret; 91 } 92 93 static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) 94 { 95 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 96 u32 value; 97 98 if (pm_runtime_status_suspended(hcd->self.controller)) 99 return 0; 100 101 /* set usbcmd.EU3S */ 102 value = readl(&xhci->op_regs->command); 103 value |= CMD_PM_INDEX; 104 writel(value, &xhci->op_regs->command); 105 106 if (hcd->regs) { 107 value = readl(hcd->regs + XECP_AUX_CTRL_REG1); 108 value |= CFG_RXDET_P3_EN; 109 writel(value, hcd->regs + XECP_AUX_CTRL_REG1); 110 111 value = readl(hcd->regs + XECP_PORT_CAP_REG); 112 value |= LPM_2_STB_SWITCH_EN; 113 writel(value, hcd->regs + XECP_PORT_CAP_REG); 114 } 115 116 return 0; 117 } 118 119 static void cdns_host_exit(struct cdns *cdns) 120 { 121 kfree(cdns->xhci_plat_data); 122 platform_device_unregister(cdns->host_dev); 123 cdns->host_dev = NULL; 124 cdns_drd_host_off(cdns); 125 } 126 127 int cdns_host_init(struct cdns *cdns) 128 { 129 struct cdns_role_driver *rdrv; 130 131 rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); 132 if (!rdrv) 133 return -ENOMEM; 134 135 rdrv->start = __cdns_host_init; 136 rdrv->stop = cdns_host_exit; 137 rdrv->state = CDNS_ROLE_STATE_INACTIVE; 138 rdrv->name = "host"; 139 140 cdns->roles[USB_ROLE_HOST] = rdrv; 141 142 return 0; 143 } 144