17733f6c3SPawel Laszczak // SPDX-License-Identifier: GPL-2.0 27733f6c3SPawel Laszczak /* 37733f6c3SPawel Laszczak * Cadence USBSS DRD Driver - host side 47733f6c3SPawel Laszczak * 57733f6c3SPawel Laszczak * Copyright (C) 2018-2019 Cadence Design Systems. 67733f6c3SPawel Laszczak * Copyright (C) 2017-2018 NXP 77733f6c3SPawel Laszczak * 87733f6c3SPawel Laszczak * Authors: Peter Chen <peter.chen@nxp.com> 97733f6c3SPawel Laszczak * Pawel Laszczak <pawell@cadence.com> 107733f6c3SPawel Laszczak */ 117733f6c3SPawel Laszczak 127733f6c3SPawel Laszczak #include <linux/platform_device.h> 137733f6c3SPawel Laszczak #include "core.h" 147733f6c3SPawel Laszczak #include "drd.h" 155053691aSBen Dooks (Codethink) #include "host-export.h" 16b1234e3bSPeter Chen #include <linux/usb/hcd.h> 17*ed227648SPeter Chen #include "../host/xhci.h" 18*ed227648SPeter Chen #include "../host/xhci-plat.h" 19*ed227648SPeter Chen 20*ed227648SPeter Chen #define XECP_PORT_CAP_REG 0x8000 21*ed227648SPeter Chen #define XECP_AUX_CTRL_REG1 0x8120 22*ed227648SPeter Chen 23*ed227648SPeter Chen #define CFG_RXDET_P3_EN BIT(15) 24*ed227648SPeter Chen #define LPM_2_STB_SWITCH_EN BIT(25) 25*ed227648SPeter Chen 26*ed227648SPeter Chen static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { 27*ed227648SPeter Chen .suspend_quirk = xhci_cdns3_suspend_quirk, 28*ed227648SPeter Chen }; 297733f6c3SPawel Laszczak 307733f6c3SPawel Laszczak static int __cdns3_host_init(struct cdns3 *cdns) 317733f6c3SPawel Laszczak { 327733f6c3SPawel Laszczak struct platform_device *xhci; 337733f6c3SPawel Laszczak int ret; 34b1234e3bSPeter Chen struct usb_hcd *hcd; 357733f6c3SPawel Laszczak 36b2aeb6daSPawel Laszczak cdns3_drd_host_on(cdns); 377733f6c3SPawel Laszczak 387733f6c3SPawel Laszczak xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); 397733f6c3SPawel Laszczak if (!xhci) { 407733f6c3SPawel Laszczak dev_err(cdns->dev, "couldn't allocate xHCI device\n"); 417733f6c3SPawel Laszczak return -ENOMEM; 427733f6c3SPawel Laszczak } 437733f6c3SPawel Laszczak 447733f6c3SPawel Laszczak xhci->dev.parent = cdns->dev; 457733f6c3SPawel Laszczak cdns->host_dev = xhci; 467733f6c3SPawel Laszczak 477733f6c3SPawel Laszczak ret = platform_device_add_resources(xhci, cdns->xhci_res, 487733f6c3SPawel Laszczak CDNS3_XHCI_RESOURCES_NUM); 497733f6c3SPawel Laszczak if (ret) { 507733f6c3SPawel Laszczak dev_err(cdns->dev, "couldn't add resources to xHCI device\n"); 517733f6c3SPawel Laszczak goto err1; 527733f6c3SPawel Laszczak } 537733f6c3SPawel Laszczak 54*ed227648SPeter Chen ret = platform_device_add_data(xhci, &xhci_plat_cdns3_xhci, 55*ed227648SPeter Chen sizeof(struct xhci_plat_priv)); 56*ed227648SPeter Chen if (ret) 57*ed227648SPeter Chen goto err1; 58*ed227648SPeter Chen 597733f6c3SPawel Laszczak ret = platform_device_add(xhci); 607733f6c3SPawel Laszczak if (ret) { 617733f6c3SPawel Laszczak dev_err(cdns->dev, "failed to register xHCI device\n"); 627733f6c3SPawel Laszczak goto err1; 637733f6c3SPawel Laszczak } 647733f6c3SPawel Laszczak 65b1234e3bSPeter Chen /* Glue needs to access xHCI region register for Power management */ 66b1234e3bSPeter Chen hcd = platform_get_drvdata(xhci); 67b1234e3bSPeter Chen if (hcd) 68b1234e3bSPeter Chen cdns->xhci_regs = hcd->regs; 69b1234e3bSPeter Chen 707733f6c3SPawel Laszczak return 0; 717733f6c3SPawel Laszczak err1: 727733f6c3SPawel Laszczak platform_device_put(xhci); 737733f6c3SPawel Laszczak return ret; 747733f6c3SPawel Laszczak } 757733f6c3SPawel Laszczak 76*ed227648SPeter Chen int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) 77*ed227648SPeter Chen { 78*ed227648SPeter Chen struct xhci_hcd *xhci = hcd_to_xhci(hcd); 79*ed227648SPeter Chen u32 value; 80*ed227648SPeter Chen 81*ed227648SPeter Chen if (pm_runtime_status_suspended(hcd->self.controller)) 82*ed227648SPeter Chen return 0; 83*ed227648SPeter Chen 84*ed227648SPeter Chen /* set usbcmd.EU3S */ 85*ed227648SPeter Chen value = readl(&xhci->op_regs->command); 86*ed227648SPeter Chen value |= CMD_PM_INDEX; 87*ed227648SPeter Chen writel(value, &xhci->op_regs->command); 88*ed227648SPeter Chen 89*ed227648SPeter Chen if (hcd->regs) { 90*ed227648SPeter Chen value = readl(hcd->regs + XECP_AUX_CTRL_REG1); 91*ed227648SPeter Chen value |= CFG_RXDET_P3_EN; 92*ed227648SPeter Chen writel(value, hcd->regs + XECP_AUX_CTRL_REG1); 93*ed227648SPeter Chen 94*ed227648SPeter Chen value = readl(hcd->regs + XECP_PORT_CAP_REG); 95*ed227648SPeter Chen value |= LPM_2_STB_SWITCH_EN; 96*ed227648SPeter Chen writel(value, hcd->regs + XECP_PORT_CAP_REG); 97*ed227648SPeter Chen } 98*ed227648SPeter Chen 99*ed227648SPeter Chen return 0; 100*ed227648SPeter Chen } 101*ed227648SPeter Chen 1027733f6c3SPawel Laszczak static void cdns3_host_exit(struct cdns3 *cdns) 1037733f6c3SPawel Laszczak { 1047733f6c3SPawel Laszczak platform_device_unregister(cdns->host_dev); 1057733f6c3SPawel Laszczak cdns->host_dev = NULL; 106b2aeb6daSPawel Laszczak cdns3_drd_host_off(cdns); 1077733f6c3SPawel Laszczak } 1087733f6c3SPawel Laszczak 1097733f6c3SPawel Laszczak int cdns3_host_init(struct cdns3 *cdns) 1107733f6c3SPawel Laszczak { 1117733f6c3SPawel Laszczak struct cdns3_role_driver *rdrv; 1127733f6c3SPawel Laszczak 1137733f6c3SPawel Laszczak rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); 1147733f6c3SPawel Laszczak if (!rdrv) 1157733f6c3SPawel Laszczak return -ENOMEM; 1167733f6c3SPawel Laszczak 1177733f6c3SPawel Laszczak rdrv->start = __cdns3_host_init; 1187733f6c3SPawel Laszczak rdrv->stop = cdns3_host_exit; 1197733f6c3SPawel Laszczak rdrv->state = CDNS3_ROLE_STATE_INACTIVE; 1207733f6c3SPawel Laszczak rdrv->name = "host"; 1217733f6c3SPawel Laszczak 1227733f6c3SPawel Laszczak cdns->roles[USB_ROLE_HOST] = rdrv; 1237733f6c3SPawel Laszczak 1247733f6c3SPawel Laszczak return 0; 1257733f6c3SPawel Laszczak } 126