Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Cadence USBSS and USBSSP DRD Driver - host side
0004  *
0005  * Copyright (C) 2018-2019 Cadence Design Systems.
0006  * Copyright (C) 2017-2018 NXP
0007  *
0008  * Authors: Peter Chen <peter.chen@nxp.com>
0009  *          Pawel Laszczak <pawell@cadence.com>
0010  */
0011 
0012 #include <linux/platform_device.h>
0013 #include <linux/slab.h>
0014 #include "core.h"
0015 #include "drd.h"
0016 #include "host-export.h"
0017 #include <linux/usb/hcd.h>
0018 #include "../host/xhci.h"
0019 #include "../host/xhci-plat.h"
0020 
0021 #define XECP_PORT_CAP_REG   0x8000
0022 #define XECP_AUX_CTRL_REG1  0x8120
0023 
0024 #define CFG_RXDET_P3_EN     BIT(15)
0025 #define LPM_2_STB_SWITCH_EN BIT(25)
0026 
0027 static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
0028 
0029 static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
0030     .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI,
0031     .suspend_quirk = xhci_cdns3_suspend_quirk,
0032 };
0033 
0034 static int __cdns_host_init(struct cdns *cdns)
0035 {
0036     struct platform_device *xhci;
0037     int ret;
0038     struct usb_hcd *hcd;
0039 
0040     cdns_drd_host_on(cdns);
0041 
0042     xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
0043     if (!xhci) {
0044         dev_err(cdns->dev, "couldn't allocate xHCI device\n");
0045         return -ENOMEM;
0046     }
0047 
0048     xhci->dev.parent = cdns->dev;
0049     cdns->host_dev = xhci;
0050 
0051     ret = platform_device_add_resources(xhci, cdns->xhci_res,
0052                         CDNS_XHCI_RESOURCES_NUM);
0053     if (ret) {
0054         dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
0055         goto err1;
0056     }
0057 
0058     cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci,
0059             sizeof(struct xhci_plat_priv), GFP_KERNEL);
0060     if (!cdns->xhci_plat_data) {
0061         ret = -ENOMEM;
0062         goto err1;
0063     }
0064 
0065     if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))
0066         cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
0067 
0068     ret = platform_device_add_data(xhci, cdns->xhci_plat_data,
0069             sizeof(struct xhci_plat_priv));
0070     if (ret)
0071         goto free_memory;
0072 
0073     ret = platform_device_add(xhci);
0074     if (ret) {
0075         dev_err(cdns->dev, "failed to register xHCI device\n");
0076         goto free_memory;
0077     }
0078 
0079     /* Glue needs to access xHCI region register for Power management */
0080     hcd = platform_get_drvdata(xhci);
0081     if (hcd)
0082         cdns->xhci_regs = hcd->regs;
0083 
0084     return 0;
0085 
0086 free_memory:
0087     kfree(cdns->xhci_plat_data);
0088 err1:
0089     platform_device_put(xhci);
0090     return ret;
0091 }
0092 
0093 static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
0094 {
0095     struct xhci_hcd *xhci = hcd_to_xhci(hcd);
0096     u32 value;
0097 
0098     if (pm_runtime_status_suspended(hcd->self.controller))
0099         return 0;
0100 
0101     /* set usbcmd.EU3S */
0102     value = readl(&xhci->op_regs->command);
0103     value |= CMD_PM_INDEX;
0104     writel(value, &xhci->op_regs->command);
0105 
0106     if (hcd->regs) {
0107         value = readl(hcd->regs + XECP_AUX_CTRL_REG1);
0108         value |= CFG_RXDET_P3_EN;
0109         writel(value, hcd->regs + XECP_AUX_CTRL_REG1);
0110 
0111         value = readl(hcd->regs + XECP_PORT_CAP_REG);
0112         value |= LPM_2_STB_SWITCH_EN;
0113         writel(value, hcd->regs + XECP_PORT_CAP_REG);
0114     }
0115 
0116     return 0;
0117 }
0118 
0119 static void cdns_host_exit(struct cdns *cdns)
0120 {
0121     kfree(cdns->xhci_plat_data);
0122     platform_device_unregister(cdns->host_dev);
0123     cdns->host_dev = NULL;
0124     cdns_drd_host_off(cdns);
0125 }
0126 
0127 int cdns_host_init(struct cdns *cdns)
0128 {
0129     struct cdns_role_driver *rdrv;
0130 
0131     rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
0132     if (!rdrv)
0133         return -ENOMEM;
0134 
0135     rdrv->start = __cdns_host_init;
0136     rdrv->stop  = cdns_host_exit;
0137     rdrv->state = CDNS_ROLE_STATE_INACTIVE;
0138     rdrv->name  = "host";
0139 
0140     cdns->roles[USB_ROLE_HOST] = rdrv;
0141 
0142     return 0;
0143 }