1 /** 2 * host.c - DesignWare USB3 DRD Controller Host Glue 3 * 4 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com 5 * 6 * Authors: Felipe Balbi <balbi@ti.com>, 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 of 10 * the License as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include <linux/platform_device.h> 19 20 #include "core.h" 21 22 static int dwc3_host_get_irq(struct dwc3 *dwc) 23 { 24 struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); 25 int irq; 26 27 irq = platform_get_irq_byname(dwc3_pdev, "host"); 28 if (irq > 0) 29 goto out; 30 31 if (irq == -EPROBE_DEFER) 32 goto out; 33 34 irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); 35 if (irq > 0) 36 goto out; 37 38 if (irq == -EPROBE_DEFER) 39 goto out; 40 41 irq = platform_get_irq(dwc3_pdev, 0); 42 if (irq > 0) 43 goto out; 44 45 if (irq != -EPROBE_DEFER) 46 dev_err(dwc->dev, "missing host IRQ\n"); 47 48 if (!irq) 49 irq = -EINVAL; 50 51 out: 52 return irq; 53 } 54 55 int dwc3_host_init(struct dwc3 *dwc) 56 { 57 struct property_entry props[3]; 58 struct platform_device *xhci; 59 int ret, irq; 60 struct resource *res; 61 struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); 62 int prop_idx = 0; 63 64 irq = dwc3_host_get_irq(dwc); 65 if (irq < 0) 66 return irq; 67 68 res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host"); 69 if (!res) 70 res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, 71 "dwc_usb3"); 72 if (!res) 73 res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0); 74 if (!res) 75 return -ENOMEM; 76 77 dwc->xhci_resources[1].start = irq; 78 dwc->xhci_resources[1].end = irq; 79 dwc->xhci_resources[1].flags = res->flags; 80 dwc->xhci_resources[1].name = res->name; 81 82 xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); 83 if (!xhci) { 84 dev_err(dwc->dev, "couldn't allocate xHCI device\n"); 85 return -ENOMEM; 86 } 87 88 xhci->dev.parent = dwc->dev; 89 90 dwc->xhci = xhci; 91 92 ret = platform_device_add_resources(xhci, dwc->xhci_resources, 93 DWC3_XHCI_RESOURCES_NUM); 94 if (ret) { 95 dev_err(dwc->dev, "couldn't add resources to xHCI device\n"); 96 goto err1; 97 } 98 99 memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props)); 100 101 if (dwc->usb3_lpm_capable) 102 props[prop_idx++].name = "usb3-lpm-capable"; 103 104 /** 105 * WORKAROUND: dwc3 revisions <=3.00a have a limitation 106 * where Port Disable command doesn't work. 107 * 108 * The suggested workaround is that we avoid Port Disable 109 * completely. 110 * 111 * This following flag tells XHCI to do just that. 112 */ 113 if (dwc->revision <= DWC3_REVISION_300A) 114 props[prop_idx++].name = "quirk-broken-port-ped"; 115 116 if (prop_idx) { 117 ret = platform_device_add_properties(xhci, props); 118 if (ret) { 119 dev_err(dwc->dev, "failed to add properties to xHCI\n"); 120 goto err1; 121 } 122 } 123 124 phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy", 125 dev_name(dwc->dev)); 126 phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy", 127 dev_name(dwc->dev)); 128 129 ret = platform_device_add(xhci); 130 if (ret) { 131 dev_err(dwc->dev, "failed to register xHCI device\n"); 132 goto err2; 133 } 134 135 return 0; 136 err2: 137 phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy", 138 dev_name(dwc->dev)); 139 phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy", 140 dev_name(dwc->dev)); 141 err1: 142 platform_device_put(xhci); 143 return ret; 144 } 145 146 void dwc3_host_exit(struct dwc3 *dwc) 147 { 148 phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy", 149 dev_name(dwc->dev)); 150 phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy", 151 dev_name(dwc->dev)); 152 platform_device_unregister(dwc->xhci); 153 } 154