18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/**
38c2ecf20Sopenharmony_ci * dwc3-keystone.c - Keystone Specific Glue layer
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2013 Texas Instruments Incorporated - https://www.ti.com
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: WingMan Kwok <w-kwok2@ti.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
178c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* USBSS register offsets */
218c2ecf20Sopenharmony_ci#define USBSS_REVISION		0x0000
228c2ecf20Sopenharmony_ci#define USBSS_SYSCONFIG		0x0010
238c2ecf20Sopenharmony_ci#define USBSS_IRQ_EOI		0x0018
248c2ecf20Sopenharmony_ci#define USBSS_IRQSTATUS_RAW_0	0x0020
258c2ecf20Sopenharmony_ci#define USBSS_IRQSTATUS_0	0x0024
268c2ecf20Sopenharmony_ci#define USBSS_IRQENABLE_SET_0	0x0028
278c2ecf20Sopenharmony_ci#define USBSS_IRQENABLE_CLR_0	0x002c
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* IRQ register bits */
308c2ecf20Sopenharmony_ci#define USBSS_IRQ_EOI_LINE(n)	BIT(n)
318c2ecf20Sopenharmony_ci#define USBSS_IRQ_EVENT_ST	BIT(0)
328c2ecf20Sopenharmony_ci#define USBSS_IRQ_COREIRQ_EN	BIT(0)
338c2ecf20Sopenharmony_ci#define USBSS_IRQ_COREIRQ_CLR	BIT(0)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct dwc3_keystone {
368c2ecf20Sopenharmony_ci	struct device			*dev;
378c2ecf20Sopenharmony_ci	void __iomem			*usbss;
388c2ecf20Sopenharmony_ci	struct phy			*usb3_phy;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline u32 kdwc3_readl(void __iomem *base, u32 offset)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return readl(base + offset);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline void kdwc3_writel(void __iomem *base, u32 offset, u32 value)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	writel(value, base + offset);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void kdwc3_enable_irqs(struct dwc3_keystone *kdwc)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	u32 val;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
568c2ecf20Sopenharmony_ci	val |= USBSS_IRQ_COREIRQ_EN;
578c2ecf20Sopenharmony_ci	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void kdwc3_disable_irqs(struct dwc3_keystone *kdwc)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	u32 val;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
658c2ecf20Sopenharmony_ci	val &= ~USBSS_IRQ_COREIRQ_EN;
668c2ecf20Sopenharmony_ci	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct dwc3_keystone	*kdwc = _kdwc;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_CLR_0, USBSS_IRQ_COREIRQ_CLR);
748c2ecf20Sopenharmony_ci	kdwc3_writel(kdwc->usbss, USBSS_IRQSTATUS_0, USBSS_IRQ_EVENT_ST);
758c2ecf20Sopenharmony_ci	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, USBSS_IRQ_COREIRQ_EN);
768c2ecf20Sopenharmony_ci	kdwc3_writel(kdwc->usbss, USBSS_IRQ_EOI, USBSS_IRQ_EOI_LINE(0));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int kdwc3_probe(struct platform_device *pdev)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct device		*dev = &pdev->dev;
848c2ecf20Sopenharmony_ci	struct device_node	*node = pdev->dev.of_node;
858c2ecf20Sopenharmony_ci	struct dwc3_keystone	*kdwc;
868c2ecf20Sopenharmony_ci	int			error, irq;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
898c2ecf20Sopenharmony_ci	if (!kdwc)
908c2ecf20Sopenharmony_ci		return -ENOMEM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, kdwc);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	kdwc->dev = dev;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	kdwc->usbss = devm_platform_ioremap_resource(pdev, 0);
978c2ecf20Sopenharmony_ci	if (IS_ERR(kdwc->usbss))
988c2ecf20Sopenharmony_ci		return PTR_ERR(kdwc->usbss);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
1018c2ecf20Sopenharmony_ci	kdwc->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
1028c2ecf20Sopenharmony_ci	if (IS_ERR(kdwc->usb3_phy)) {
1038c2ecf20Sopenharmony_ci		error = PTR_ERR(kdwc->usb3_phy);
1048c2ecf20Sopenharmony_ci		if (error != -EPROBE_DEFER)
1058c2ecf20Sopenharmony_ci			dev_err(dev, "couldn't get usb3 phy: %d\n", error);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		return error;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	phy_pm_runtime_get_sync(kdwc->usb3_phy);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	error = phy_reset(kdwc->usb3_phy);
1138c2ecf20Sopenharmony_ci	if (error < 0) {
1148c2ecf20Sopenharmony_ci		dev_err(dev, "usb3 phy reset failed: %d\n", error);
1158c2ecf20Sopenharmony_ci		return error;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	error = phy_init(kdwc->usb3_phy);
1198c2ecf20Sopenharmony_ci	if (error < 0) {
1208c2ecf20Sopenharmony_ci		dev_err(dev, "usb3 phy init failed: %d\n", error);
1218c2ecf20Sopenharmony_ci		return error;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	error = phy_power_on(kdwc->usb3_phy);
1258c2ecf20Sopenharmony_ci	if (error < 0) {
1268c2ecf20Sopenharmony_ci		dev_err(dev, "usb3 phy power on failed: %d\n", error);
1278c2ecf20Sopenharmony_ci		phy_exit(kdwc->usb3_phy);
1288c2ecf20Sopenharmony_ci		return error;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	pm_runtime_enable(kdwc->dev);
1328c2ecf20Sopenharmony_ci	error = pm_runtime_get_sync(kdwc->dev);
1338c2ecf20Sopenharmony_ci	if (error < 0) {
1348c2ecf20Sopenharmony_ci		dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
1358c2ecf20Sopenharmony_ci			error);
1368c2ecf20Sopenharmony_ci		goto err_irq;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* IRQ processing not required currently for AM65 */
1408c2ecf20Sopenharmony_ci	if (of_device_is_compatible(node, "ti,am654-dwc3"))
1418c2ecf20Sopenharmony_ci		goto skip_irq;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
1448c2ecf20Sopenharmony_ci	if (irq < 0) {
1458c2ecf20Sopenharmony_ci		error = irq;
1468c2ecf20Sopenharmony_ci		goto err_irq;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED,
1508c2ecf20Sopenharmony_ci			dev_name(dev), kdwc);
1518c2ecf20Sopenharmony_ci	if (error) {
1528c2ecf20Sopenharmony_ci		dev_err(dev, "failed to request IRQ #%d --> %d\n",
1538c2ecf20Sopenharmony_ci				irq, error);
1548c2ecf20Sopenharmony_ci		goto err_irq;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	kdwc3_enable_irqs(kdwc);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciskip_irq:
1608c2ecf20Sopenharmony_ci	error = of_platform_populate(node, NULL, NULL, dev);
1618c2ecf20Sopenharmony_ci	if (error) {
1628c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to create dwc3 core\n");
1638c2ecf20Sopenharmony_ci		goto err_core;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cierr_core:
1698c2ecf20Sopenharmony_ci	kdwc3_disable_irqs(kdwc);
1708c2ecf20Sopenharmony_cierr_irq:
1718c2ecf20Sopenharmony_ci	pm_runtime_put_sync(kdwc->dev);
1728c2ecf20Sopenharmony_ci	pm_runtime_disable(kdwc->dev);
1738c2ecf20Sopenharmony_ci	phy_power_off(kdwc->usb3_phy);
1748c2ecf20Sopenharmony_ci	phy_exit(kdwc->usb3_phy);
1758c2ecf20Sopenharmony_ci	phy_pm_runtime_put_sync(kdwc->usb3_phy);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return error;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int kdwc3_remove_core(struct device *dev, void *c)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	platform_device_unregister(pdev);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int kdwc3_remove(struct platform_device *pdev)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
1928c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (!of_device_is_compatible(node, "ti,am654-dwc3"))
1958c2ecf20Sopenharmony_ci		kdwc3_disable_irqs(kdwc);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core);
1988c2ecf20Sopenharmony_ci	pm_runtime_put_sync(kdwc->dev);
1998c2ecf20Sopenharmony_ci	pm_runtime_disable(kdwc->dev);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	phy_power_off(kdwc->usb3_phy);
2028c2ecf20Sopenharmony_ci	phy_exit(kdwc->usb3_phy);
2038c2ecf20Sopenharmony_ci	phy_pm_runtime_put_sync(kdwc->usb3_phy);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return 0;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic const struct of_device_id kdwc3_of_match[] = {
2118c2ecf20Sopenharmony_ci	{ .compatible = "ti,keystone-dwc3", },
2128c2ecf20Sopenharmony_ci	{ .compatible = "ti,am654-dwc3" },
2138c2ecf20Sopenharmony_ci	{},
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, kdwc3_of_match);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic struct platform_driver kdwc3_driver = {
2188c2ecf20Sopenharmony_ci	.probe		= kdwc3_probe,
2198c2ecf20Sopenharmony_ci	.remove		= kdwc3_remove,
2208c2ecf20Sopenharmony_ci	.driver		= {
2218c2ecf20Sopenharmony_ci		.name	= "keystone-dwc3",
2228c2ecf20Sopenharmony_ci		.of_match_table	= kdwc3_of_match,
2238c2ecf20Sopenharmony_ci	},
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cimodule_platform_driver(kdwc3_driver);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:keystone-dwc3");
2298c2ecf20Sopenharmony_ciMODULE_AUTHOR("WingMan Kwok <w-kwok2@ti.com>");
2308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer");
232