18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * host.c - DesignWare USB3 DRD Controller Host Glue
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors: Felipe Balbi <balbi@ti.com>,
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/acpi.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "core.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic int dwc3_host_get_irq(struct dwc3 *dwc)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
188c2ecf20Sopenharmony_ci	int irq;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	irq = platform_get_irq_byname_optional(dwc3_pdev, "host");
218c2ecf20Sopenharmony_ci	if (irq > 0)
228c2ecf20Sopenharmony_ci		goto out;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (irq == -EPROBE_DEFER)
258c2ecf20Sopenharmony_ci		goto out;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
288c2ecf20Sopenharmony_ci	if (irq > 0)
298c2ecf20Sopenharmony_ci		goto out;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (irq == -EPROBE_DEFER)
328c2ecf20Sopenharmony_ci		goto out;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	irq = platform_get_irq(dwc3_pdev, 0);
358c2ecf20Sopenharmony_ci	if (irq > 0)
368c2ecf20Sopenharmony_ci		goto out;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (!irq)
398c2ecf20Sopenharmony_ci		irq = -EINVAL;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciout:
428c2ecf20Sopenharmony_ci	return irq;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint dwc3_host_init(struct dwc3 *dwc)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct property_entry	props[4];
488c2ecf20Sopenharmony_ci	struct platform_device	*xhci;
498c2ecf20Sopenharmony_ci	int			ret, irq;
508c2ecf20Sopenharmony_ci	struct resource		*res;
518c2ecf20Sopenharmony_ci	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
528c2ecf20Sopenharmony_ci	int			prop_idx = 0;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	irq = dwc3_host_get_irq(dwc);
558c2ecf20Sopenharmony_ci	if (irq < 0)
568c2ecf20Sopenharmony_ci		return irq;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
598c2ecf20Sopenharmony_ci	if (!res)
608c2ecf20Sopenharmony_ci		res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
618c2ecf20Sopenharmony_ci				"dwc_usb3");
628c2ecf20Sopenharmony_ci	if (!res)
638c2ecf20Sopenharmony_ci		res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
648c2ecf20Sopenharmony_ci	if (!res)
658c2ecf20Sopenharmony_ci		return -ENOMEM;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	dwc->xhci_resources[1].start = irq;
688c2ecf20Sopenharmony_ci	dwc->xhci_resources[1].end = irq;
698c2ecf20Sopenharmony_ci	dwc->xhci_resources[1].flags = res->flags;
708c2ecf20Sopenharmony_ci	dwc->xhci_resources[1].name = res->name;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
738c2ecf20Sopenharmony_ci	if (!xhci) {
748c2ecf20Sopenharmony_ci		dev_err(dwc->dev, "couldn't allocate xHCI device\n");
758c2ecf20Sopenharmony_ci		return -ENOMEM;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	xhci->dev.parent	= dwc->dev;
798c2ecf20Sopenharmony_ci	ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev));
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	dwc->xhci = xhci;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	ret = platform_device_add_resources(xhci, dwc->xhci_resources,
848c2ecf20Sopenharmony_ci						DWC3_XHCI_RESOURCES_NUM);
858c2ecf20Sopenharmony_ci	if (ret) {
868c2ecf20Sopenharmony_ci		dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
878c2ecf20Sopenharmony_ci		goto err;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (dwc->usb3_lpm_capable)
938c2ecf20Sopenharmony_ci		props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (dwc->usb2_lpm_disable)
968c2ecf20Sopenharmony_ci		props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb2-lpm-disable");
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/**
998c2ecf20Sopenharmony_ci	 * WORKAROUND: dwc3 revisions <=3.00a have a limitation
1008c2ecf20Sopenharmony_ci	 * where Port Disable command doesn't work.
1018c2ecf20Sopenharmony_ci	 *
1028c2ecf20Sopenharmony_ci	 * The suggested workaround is that we avoid Port Disable
1038c2ecf20Sopenharmony_ci	 * completely.
1048c2ecf20Sopenharmony_ci	 *
1058c2ecf20Sopenharmony_ci	 * This following flag tells XHCI to do just that.
1068c2ecf20Sopenharmony_ci	 */
1078c2ecf20Sopenharmony_ci	if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
1088c2ecf20Sopenharmony_ci		props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (prop_idx) {
1118c2ecf20Sopenharmony_ci		ret = platform_device_add_properties(xhci, props);
1128c2ecf20Sopenharmony_ci		if (ret) {
1138c2ecf20Sopenharmony_ci			dev_err(dwc->dev, "failed to add properties to xHCI\n");
1148c2ecf20Sopenharmony_ci			goto err;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ret = platform_device_add(xhci);
1198c2ecf20Sopenharmony_ci	if (ret) {
1208c2ecf20Sopenharmony_ci		dev_err(dwc->dev, "failed to register xHCI device\n");
1218c2ecf20Sopenharmony_ci		goto err;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_cierr:
1268c2ecf20Sopenharmony_ci	platform_device_put(xhci);
1278c2ecf20Sopenharmony_ci	return ret;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid dwc3_host_exit(struct dwc3 *dwc)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	platform_device_unregister(dwc->xhci);
1338c2ecf20Sopenharmony_ci	dwc->xhci = NULL;
1348c2ecf20Sopenharmony_ci}
135