David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Cadence USBSS 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 "core.h" |
| 14 | #include "drd.h" |
| 15 | #include "host-export.h" |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 16 | #include <linux/usb/hcd.h> |
| 17 | #include "../host/xhci.h" |
| 18 | #include "../host/xhci-plat.h" |
| 19 | |
| 20 | #define XECP_PORT_CAP_REG 0x8000 |
| 21 | #define XECP_AUX_CTRL_REG1 0x8120 |
| 22 | |
| 23 | #define CFG_RXDET_P3_EN BIT(15) |
| 24 | #define LPM_2_STB_SWITCH_EN BIT(25) |
| 25 | |
| 26 | static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { |
| 27 | .quirks = XHCI_SKIP_PHY_INIT, |
| 28 | .suspend_quirk = xhci_cdns3_suspend_quirk, |
| 29 | }; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 30 | |
| 31 | static int __cdns3_host_init(struct cdns3 *cdns) |
| 32 | { |
| 33 | struct platform_device *xhci; |
| 34 | int ret; |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 35 | struct usb_hcd *hcd; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 36 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 37 | cdns3_drd_host_on(cdns); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 38 | |
| 39 | xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); |
| 40 | if (!xhci) { |
| 41 | dev_err(cdns->dev, "couldn't allocate xHCI device\n"); |
| 42 | return -ENOMEM; |
| 43 | } |
| 44 | |
| 45 | xhci->dev.parent = cdns->dev; |
| 46 | cdns->host_dev = xhci; |
| 47 | |
| 48 | ret = platform_device_add_resources(xhci, cdns->xhci_res, |
| 49 | CDNS3_XHCI_RESOURCES_NUM); |
| 50 | if (ret) { |
| 51 | dev_err(cdns->dev, "couldn't add resources to xHCI device\n"); |
| 52 | goto err1; |
| 53 | } |
| 54 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 55 | cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci, |
| 56 | sizeof(struct xhci_plat_priv), GFP_KERNEL); |
| 57 | if (!cdns->xhci_plat_data) { |
| 58 | ret = -ENOMEM; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 59 | goto err1; |
| 60 | } |
| 61 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 62 | if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) |
| 63 | cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; |
| 64 | |
| 65 | ret = platform_device_add_data(xhci, cdns->xhci_plat_data, |
| 66 | sizeof(struct xhci_plat_priv)); |
| 67 | if (ret) |
| 68 | goto free_memory; |
| 69 | |
| 70 | ret = platform_device_add(xhci); |
| 71 | if (ret) { |
| 72 | dev_err(cdns->dev, "failed to register xHCI device\n"); |
| 73 | goto free_memory; |
| 74 | } |
| 75 | |
| 76 | /* Glue needs to access xHCI region register for Power management */ |
| 77 | hcd = platform_get_drvdata(xhci); |
| 78 | if (hcd) |
| 79 | cdns->xhci_regs = hcd->regs; |
| 80 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 81 | return 0; |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 82 | |
| 83 | free_memory: |
| 84 | kfree(cdns->xhci_plat_data); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 85 | err1: |
| 86 | platform_device_put(xhci); |
| 87 | return ret; |
| 88 | } |
| 89 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 90 | int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) |
| 91 | { |
| 92 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| 93 | u32 value; |
| 94 | |
| 95 | if (pm_runtime_status_suspended(hcd->self.controller)) |
| 96 | return 0; |
| 97 | |
| 98 | /* set usbcmd.EU3S */ |
| 99 | value = readl(&xhci->op_regs->command); |
| 100 | value |= CMD_PM_INDEX; |
| 101 | writel(value, &xhci->op_regs->command); |
| 102 | |
| 103 | if (hcd->regs) { |
| 104 | value = readl(hcd->regs + XECP_AUX_CTRL_REG1); |
| 105 | value |= CFG_RXDET_P3_EN; |
| 106 | writel(value, hcd->regs + XECP_AUX_CTRL_REG1); |
| 107 | |
| 108 | value = readl(hcd->regs + XECP_PORT_CAP_REG); |
| 109 | value |= LPM_2_STB_SWITCH_EN; |
| 110 | writel(value, hcd->regs + XECP_PORT_CAP_REG); |
| 111 | } |
| 112 | |
| 113 | return 0; |
| 114 | } |
| 115 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 116 | static void cdns3_host_exit(struct cdns3 *cdns) |
| 117 | { |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 118 | kfree(cdns->xhci_plat_data); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 119 | platform_device_unregister(cdns->host_dev); |
| 120 | cdns->host_dev = NULL; |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 121 | cdns3_drd_host_off(cdns); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | int cdns3_host_init(struct cdns3 *cdns) |
| 125 | { |
| 126 | struct cdns3_role_driver *rdrv; |
| 127 | |
| 128 | rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); |
| 129 | if (!rdrv) |
| 130 | return -ENOMEM; |
| 131 | |
| 132 | rdrv->start = __cdns3_host_init; |
| 133 | rdrv->stop = cdns3_host_exit; |
| 134 | rdrv->state = CDNS3_ROLE_STATE_INACTIVE; |
| 135 | rdrv->name = "host"; |
| 136 | |
| 137 | cdns->roles[USB_ROLE_HOST] = rdrv; |
| 138 | |
| 139 | return 0; |
| 140 | } |