xref: /openbmc/linux/drivers/usb/host/ehci-exynos.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
229824c16SJingoo Han /*
3dea7b202SKrzysztof Kozlowski  * Samsung Exynos USB HOST EHCI Controller
429824c16SJingoo Han  *
529824c16SJingoo Han  * Copyright (C) 2011 Samsung Electronics Co.Ltd
629824c16SJingoo Han  * Author: Jingoo Han <jg1.han@samsung.com>
729824c16SJingoo Han  * Author: Joonyoung Shim <jy0922.shim@samsung.com>
829824c16SJingoo Han  */
929824c16SJingoo Han 
1029824c16SJingoo Han #include <linux/clk.h>
1129824c16SJingoo Han #include <linux/dma-mapping.h>
1229824c16SJingoo Han #include <linux/io.h>
1329824c16SJingoo Han #include <linux/kernel.h>
1429824c16SJingoo Han #include <linux/module.h>
1529824c16SJingoo Han #include <linux/of.h>
16a0d381caSDmitry Torokhov #include <linux/gpio/consumer.h>
171c17675dSKamil Debski #include <linux/phy/phy.h>
1829824c16SJingoo Han #include <linux/platform_device.h>
1929824c16SJingoo Han #include <linux/usb.h>
2029824c16SJingoo Han #include <linux/usb/hcd.h>
2129824c16SJingoo Han 
2229824c16SJingoo Han #include "ehci.h"
2329824c16SJingoo Han 
24dea7b202SKrzysztof Kozlowski #define DRIVER_DESC "EHCI Exynos driver"
2529824c16SJingoo Han 
2629824c16SJingoo Han #define EHCI_INSNREG00(base)			(base + 0x90)
2729824c16SJingoo Han #define EHCI_INSNREG00_ENA_INCR16		(0x1 << 25)
2829824c16SJingoo Han #define EHCI_INSNREG00_ENA_INCR8		(0x1 << 24)
2929824c16SJingoo Han #define EHCI_INSNREG00_ENA_INCR4		(0x1 << 23)
3029824c16SJingoo Han #define EHCI_INSNREG00_ENA_INCRX_ALIGN		(0x1 << 22)
3129824c16SJingoo Han #define EHCI_INSNREG00_ENABLE_DMA_BURST	\
3229824c16SJingoo Han 	(EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 |	\
3329824c16SJingoo Han 	 EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
3429824c16SJingoo Han 
3529824c16SJingoo Han static struct hc_driver __read_mostly exynos_ehci_hc_driver;
3629824c16SJingoo Han 
371c17675dSKamil Debski #define PHY_NUMBER 3
381c17675dSKamil Debski 
3929824c16SJingoo Han struct exynos_ehci_hcd {
4029824c16SJingoo Han 	struct clk *clk;
4101d40714SMarek Szyprowski 	struct device_node *of_node;
4246c1cda8SVivek Gautam 	struct phy *phy[PHY_NUMBER];
43214b606eSMarek Szyprowski 	bool legacy_phy;
4429824c16SJingoo Han };
4529824c16SJingoo Han 
4629824c16SJingoo Han #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
4729824c16SJingoo Han 
exynos_ehci_get_phy(struct device * dev,struct exynos_ehci_hcd * exynos_ehci)481c17675dSKamil Debski static int exynos_ehci_get_phy(struct device *dev,
491c17675dSKamil Debski 				struct exynos_ehci_hcd *exynos_ehci)
501c17675dSKamil Debski {
511c17675dSKamil Debski 	struct device_node *child;
521c17675dSKamil Debski 	struct phy *phy;
53214b606eSMarek Szyprowski 	int phy_number, num_phys;
5446c1cda8SVivek Gautam 	int ret;
551c17675dSKamil Debski 
5646c1cda8SVivek Gautam 	/* Get PHYs for the controller */
57214b606eSMarek Szyprowski 	num_phys = of_count_phandle_with_args(dev->of_node, "phys",
58214b606eSMarek Szyprowski 					      "#phy-cells");
59214b606eSMarek Szyprowski 	for (phy_number = 0; phy_number < num_phys; phy_number++) {
60214b606eSMarek Szyprowski 		phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number);
61214b606eSMarek Szyprowski 		if (IS_ERR(phy))
62214b606eSMarek Szyprowski 			return PTR_ERR(phy);
63214b606eSMarek Szyprowski 		exynos_ehci->phy[phy_number] = phy;
64214b606eSMarek Szyprowski 	}
65214b606eSMarek Szyprowski 	if (num_phys > 0)
66214b606eSMarek Szyprowski 		return 0;
67214b606eSMarek Szyprowski 
68214b606eSMarek Szyprowski 	/* Get PHYs using legacy bindings */
691c17675dSKamil Debski 	for_each_available_child_of_node(dev->of_node, child) {
701c17675dSKamil Debski 		ret = of_property_read_u32(child, "reg", &phy_number);
711c17675dSKamil Debski 		if (ret) {
721c17675dSKamil Debski 			dev_err(dev, "Failed to parse device tree\n");
731c17675dSKamil Debski 			of_node_put(child);
741c17675dSKamil Debski 			return ret;
751c17675dSKamil Debski 		}
761c17675dSKamil Debski 
771c17675dSKamil Debski 		if (phy_number >= PHY_NUMBER) {
781c17675dSKamil Debski 			dev_err(dev, "Invalid number of PHYs\n");
791c17675dSKamil Debski 			of_node_put(child);
801c17675dSKamil Debski 			return -EINVAL;
811c17675dSKamil Debski 		}
821c17675dSKamil Debski 
8386a17684SGeert Uytterhoeven 		phy = devm_of_phy_optional_get(dev, child, NULL);
8446c1cda8SVivek Gautam 		exynos_ehci->phy[phy_number] = phy;
8546c1cda8SVivek Gautam 		if (IS_ERR(phy)) {
863f6026b1SKrzysztof Kozlowski 			of_node_put(child);
8786a17684SGeert Uytterhoeven 			return PTR_ERR(phy);
8846c1cda8SVivek Gautam 		}
892f7f41c7SVivek Gautam 	}
902f7f41c7SVivek Gautam 
91214b606eSMarek Szyprowski 	exynos_ehci->legacy_phy = true;
922f7f41c7SVivek Gautam 	return 0;
931c17675dSKamil Debski }
941c17675dSKamil Debski 
exynos_ehci_phy_enable(struct device * dev)951c17675dSKamil Debski static int exynos_ehci_phy_enable(struct device *dev)
961c17675dSKamil Debski {
971c17675dSKamil Debski 	struct usb_hcd *hcd = dev_get_drvdata(dev);
981c17675dSKamil Debski 	struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
991c17675dSKamil Debski 	int i;
1001c17675dSKamil Debski 	int ret = 0;
1011c17675dSKamil Debski 
1021c17675dSKamil Debski 	for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
10346c1cda8SVivek Gautam 		ret = phy_power_on(exynos_ehci->phy[i]);
1041c17675dSKamil Debski 	if (ret)
1051c17675dSKamil Debski 		for (i--; i >= 0; i--)
10646c1cda8SVivek Gautam 			phy_power_off(exynos_ehci->phy[i]);
1071c17675dSKamil Debski 
1081c17675dSKamil Debski 	return ret;
1091c17675dSKamil Debski }
1101c17675dSKamil Debski 
exynos_ehci_phy_disable(struct device * dev)1111c17675dSKamil Debski static void exynos_ehci_phy_disable(struct device *dev)
1121c17675dSKamil Debski {
1131c17675dSKamil Debski 	struct usb_hcd *hcd = dev_get_drvdata(dev);
1141c17675dSKamil Debski 	struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
1151c17675dSKamil Debski 	int i;
1161c17675dSKamil Debski 
1171c17675dSKamil Debski 	for (i = 0; i < PHY_NUMBER; i++)
11846c1cda8SVivek Gautam 		phy_power_off(exynos_ehci->phy[i]);
1191c17675dSKamil Debski }
1201c17675dSKamil Debski 
exynos_setup_vbus_gpio(struct device * dev)12191a9677aSVivek Gautam static void exynos_setup_vbus_gpio(struct device *dev)
12229824c16SJingoo Han {
123a0d381caSDmitry Torokhov 	struct gpio_desc *gpio;
12429824c16SJingoo Han 	int err;
12529824c16SJingoo Han 
126a0d381caSDmitry Torokhov 	gpio = devm_gpiod_get_optional(dev, "samsung,vbus", GPIOD_OUT_HIGH);
127a0d381caSDmitry Torokhov 	err = PTR_ERR_OR_ZERO(gpio);
12829824c16SJingoo Han 	if (err)
129a0d381caSDmitry Torokhov 		dev_err(dev, "can't request ehci vbus gpio: %d\n", err);
13029824c16SJingoo Han }
13129824c16SJingoo Han 
exynos_ehci_probe(struct platform_device * pdev)13229824c16SJingoo Han static int exynos_ehci_probe(struct platform_device *pdev)
13329824c16SJingoo Han {
13429824c16SJingoo Han 	struct exynos_ehci_hcd *exynos_ehci;
13529824c16SJingoo Han 	struct usb_hcd *hcd;
13629824c16SJingoo Han 	struct ehci_hcd *ehci;
13729824c16SJingoo Han 	struct resource *res;
13829824c16SJingoo Han 	int irq;
13929824c16SJingoo Han 	int err;
14029824c16SJingoo Han 
14129824c16SJingoo Han 	/*
14229824c16SJingoo Han 	 * Right now device-tree probed devices don't get dma_mask set.
14329824c16SJingoo Han 	 * Since shared usb code relies on it, set it here for now.
14429824c16SJingoo Han 	 * Once we move to full device tree support this will vanish off.
14529824c16SJingoo Han 	 */
1468ceafbfaSLinus Torvalds 	err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
1478ceafbfaSLinus Torvalds 	if (err)
1488ceafbfaSLinus Torvalds 		return err;
14929824c16SJingoo Han 
15091a9677aSVivek Gautam 	exynos_setup_vbus_gpio(&pdev->dev);
15129824c16SJingoo Han 
15229824c16SJingoo Han 	hcd = usb_create_hcd(&exynos_ehci_hc_driver,
15329824c16SJingoo Han 			     &pdev->dev, dev_name(&pdev->dev));
15429824c16SJingoo Han 	if (!hcd) {
15529824c16SJingoo Han 		dev_err(&pdev->dev, "Unable to create HCD\n");
15629824c16SJingoo Han 		return -ENOMEM;
15729824c16SJingoo Han 	}
15829824c16SJingoo Han 	exynos_ehci = to_exynos_ehci(hcd);
15929824c16SJingoo Han 
1601c17675dSKamil Debski 	err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci);
1611c17675dSKamil Debski 	if (err)
1621c17675dSKamil Debski 		goto fail_clk;
16329824c16SJingoo Han 
16429824c16SJingoo Han 	exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
16529824c16SJingoo Han 
16629824c16SJingoo Han 	if (IS_ERR(exynos_ehci->clk)) {
16729824c16SJingoo Han 		dev_err(&pdev->dev, "Failed to get usbhost clock\n");
16829824c16SJingoo Han 		err = PTR_ERR(exynos_ehci->clk);
16929824c16SJingoo Han 		goto fail_clk;
17029824c16SJingoo Han 	}
17129824c16SJingoo Han 
17229824c16SJingoo Han 	err = clk_prepare_enable(exynos_ehci->clk);
17329824c16SJingoo Han 	if (err)
17429824c16SJingoo Han 		goto fail_clk;
17529824c16SJingoo Han 
176*61baaa65SYangtao Li 	hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
1774e24bde3SVivek Gautam 	if (IS_ERR(hcd->regs)) {
1784e24bde3SVivek Gautam 		err = PTR_ERR(hcd->regs);
17929824c16SJingoo Han 		goto fail_io;
18029824c16SJingoo Han 	}
18129824c16SJingoo Han 
18235df647cSVarka Bhadram 	hcd->rsrc_start = res->start;
18335df647cSVarka Bhadram 	hcd->rsrc_len = resource_size(res);
18435df647cSVarka Bhadram 
18529824c16SJingoo Han 	irq = platform_get_irq(pdev, 0);
18644ed240dSTang Bin 	if (irq < 0) {
18744ed240dSTang Bin 		err = irq;
18829824c16SJingoo Han 		goto fail_io;
18929824c16SJingoo Han 	}
19029824c16SJingoo Han 
1911c17675dSKamil Debski 	err = exynos_ehci_phy_enable(&pdev->dev);
1921c17675dSKamil Debski 	if (err) {
1931c17675dSKamil Debski 		dev_err(&pdev->dev, "Failed to enable USB phy\n");
1941c17675dSKamil Debski 		goto fail_io;
1951c17675dSKamil Debski 	}
19629824c16SJingoo Han 
19729824c16SJingoo Han 	ehci = hcd_to_ehci(hcd);
19829824c16SJingoo Han 	ehci->caps = hcd->regs;
19929824c16SJingoo Han 
20001d40714SMarek Szyprowski 	/*
201214b606eSMarek Szyprowski 	 * Workaround: reset of_node pointer to avoid conflict between legacy
202214b606eSMarek Szyprowski 	 * Exynos EHCI port subnodes and generic USB device bindings
20301d40714SMarek Szyprowski 	 */
20401d40714SMarek Szyprowski 	exynos_ehci->of_node = pdev->dev.of_node;
205214b606eSMarek Szyprowski 	if (exynos_ehci->legacy_phy)
20601d40714SMarek Szyprowski 		pdev->dev.of_node = NULL;
20701d40714SMarek Szyprowski 
20829824c16SJingoo Han 	/* DMA burst Enable */
20929824c16SJingoo Han 	writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
21029824c16SJingoo Han 
21129824c16SJingoo Han 	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
21229824c16SJingoo Han 	if (err) {
21329824c16SJingoo Han 		dev_err(&pdev->dev, "Failed to add USB HCD\n");
21429824c16SJingoo Han 		goto fail_add_hcd;
21529824c16SJingoo Han 	}
2163c9740a1SPeter Chen 	device_wakeup_enable(hcd->self.controller);
21729824c16SJingoo Han 
21829824c16SJingoo Han 	platform_set_drvdata(pdev, hcd);
21929824c16SJingoo Han 
22029824c16SJingoo Han 	return 0;
22129824c16SJingoo Han 
22229824c16SJingoo Han fail_add_hcd:
2231c17675dSKamil Debski 	exynos_ehci_phy_disable(&pdev->dev);
22401d40714SMarek Szyprowski 	pdev->dev.of_node = exynos_ehci->of_node;
22529824c16SJingoo Han fail_io:
22629824c16SJingoo Han 	clk_disable_unprepare(exynos_ehci->clk);
22729824c16SJingoo Han fail_clk:
22829824c16SJingoo Han 	usb_put_hcd(hcd);
22929824c16SJingoo Han 	return err;
23029824c16SJingoo Han }
23129824c16SJingoo Han 
exynos_ehci_remove(struct platform_device * pdev)2321043c6baSUwe Kleine-König static void exynos_ehci_remove(struct platform_device *pdev)
23329824c16SJingoo Han {
23429824c16SJingoo Han 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
23529824c16SJingoo Han 	struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
23629824c16SJingoo Han 
23701d40714SMarek Szyprowski 	pdev->dev.of_node = exynos_ehci->of_node;
23801d40714SMarek Szyprowski 
23929824c16SJingoo Han 	usb_remove_hcd(hcd);
24029824c16SJingoo Han 
2411c17675dSKamil Debski 	exynos_ehci_phy_disable(&pdev->dev);
24229824c16SJingoo Han 
24329824c16SJingoo Han 	clk_disable_unprepare(exynos_ehci->clk);
24429824c16SJingoo Han 
24529824c16SJingoo Han 	usb_put_hcd(hcd);
24629824c16SJingoo Han }
24729824c16SJingoo Han 
24829824c16SJingoo Han #ifdef CONFIG_PM
exynos_ehci_suspend(struct device * dev)24929824c16SJingoo Han static int exynos_ehci_suspend(struct device *dev)
25029824c16SJingoo Han {
25129824c16SJingoo Han 	struct usb_hcd *hcd = dev_get_drvdata(dev);
25229824c16SJingoo Han 	struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
25329824c16SJingoo Han 
25429824c16SJingoo Han 	bool do_wakeup = device_may_wakeup(dev);
25529824c16SJingoo Han 	int rc;
25629824c16SJingoo Han 
25729824c16SJingoo Han 	rc = ehci_suspend(hcd, do_wakeup);
258d7217510SVivek Gautam 	if (rc)
259d7217510SVivek Gautam 		return rc;
26029824c16SJingoo Han 
2611c17675dSKamil Debski 	exynos_ehci_phy_disable(dev);
26229824c16SJingoo Han 
26329824c16SJingoo Han 	clk_disable_unprepare(exynos_ehci->clk);
26429824c16SJingoo Han 
26529824c16SJingoo Han 	return rc;
26629824c16SJingoo Han }
26729824c16SJingoo Han 
exynos_ehci_resume(struct device * dev)26829824c16SJingoo Han static int exynos_ehci_resume(struct device *dev)
26929824c16SJingoo Han {
27029824c16SJingoo Han 	struct usb_hcd *hcd = dev_get_drvdata(dev);
27129824c16SJingoo Han 	struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
2721c17675dSKamil Debski 	int ret;
27329824c16SJingoo Han 
274264ffb19SArvind Yadav 	ret = clk_prepare_enable(exynos_ehci->clk);
275264ffb19SArvind Yadav 	if (ret)
276264ffb19SArvind Yadav 		return ret;
27729824c16SJingoo Han 
2781c17675dSKamil Debski 	ret = exynos_ehci_phy_enable(dev);
2791c17675dSKamil Debski 	if (ret) {
2801c17675dSKamil Debski 		dev_err(dev, "Failed to enable USB phy\n");
2811c17675dSKamil Debski 		clk_disable_unprepare(exynos_ehci->clk);
2821c17675dSKamil Debski 		return ret;
2831c17675dSKamil Debski 	}
28429824c16SJingoo Han 
28529824c16SJingoo Han 	/* DMA burst Enable */
28629824c16SJingoo Han 	writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
28729824c16SJingoo Han 
28829824c16SJingoo Han 	ehci_resume(hcd, false);
28929824c16SJingoo Han 	return 0;
29029824c16SJingoo Han }
29129824c16SJingoo Han #else
29229824c16SJingoo Han #define exynos_ehci_suspend	NULL
29329824c16SJingoo Han #define exynos_ehci_resume	NULL
29429824c16SJingoo Han #endif
29529824c16SJingoo Han 
29629824c16SJingoo Han static const struct dev_pm_ops exynos_ehci_pm_ops = {
29729824c16SJingoo Han 	.suspend	= exynos_ehci_suspend,
29829824c16SJingoo Han 	.resume		= exynos_ehci_resume,
29929824c16SJingoo Han };
30029824c16SJingoo Han 
30129824c16SJingoo Han #ifdef CONFIG_OF
30229824c16SJingoo Han static const struct of_device_id exynos_ehci_match[] = {
30329824c16SJingoo Han 	{ .compatible = "samsung,exynos4210-ehci" },
30429824c16SJingoo Han 	{},
30529824c16SJingoo Han };
30629824c16SJingoo Han MODULE_DEVICE_TABLE(of, exynos_ehci_match);
30729824c16SJingoo Han #endif
30829824c16SJingoo Han 
30929824c16SJingoo Han static struct platform_driver exynos_ehci_driver = {
31029824c16SJingoo Han 	.probe		= exynos_ehci_probe,
3111043c6baSUwe Kleine-König 	.remove_new	= exynos_ehci_remove,
31229824c16SJingoo Han 	.shutdown	= usb_hcd_platform_shutdown,
31329824c16SJingoo Han 	.driver = {
31429824c16SJingoo Han 		.name	= "exynos-ehci",
31529824c16SJingoo Han 		.pm	= &exynos_ehci_pm_ops,
31629824c16SJingoo Han 		.of_match_table = of_match_ptr(exynos_ehci_match),
31729824c16SJingoo Han 	}
31829824c16SJingoo Han };
319edc8c54bSNicolas Pitre static const struct ehci_driver_overrides exynos_overrides __initconst = {
32029824c16SJingoo Han 	.extra_priv_size = sizeof(struct exynos_ehci_hcd),
32129824c16SJingoo Han };
32229824c16SJingoo Han 
ehci_exynos_init(void)32329824c16SJingoo Han static int __init ehci_exynos_init(void)
32429824c16SJingoo Han {
32529824c16SJingoo Han 	if (usb_disabled())
32629824c16SJingoo Han 		return -ENODEV;
32729824c16SJingoo Han 
32829824c16SJingoo Han 	ehci_init_driver(&exynos_ehci_hc_driver, &exynos_overrides);
32929824c16SJingoo Han 	return platform_driver_register(&exynos_ehci_driver);
33029824c16SJingoo Han }
33129824c16SJingoo Han module_init(ehci_exynos_init);
33229824c16SJingoo Han 
ehci_exynos_cleanup(void)33329824c16SJingoo Han static void __exit ehci_exynos_cleanup(void)
33429824c16SJingoo Han {
33529824c16SJingoo Han 	platform_driver_unregister(&exynos_ehci_driver);
33629824c16SJingoo Han }
33729824c16SJingoo Han module_exit(ehci_exynos_cleanup);
33829824c16SJingoo Han 
33929824c16SJingoo Han MODULE_DESCRIPTION(DRIVER_DESC);
34029824c16SJingoo Han MODULE_ALIAS("platform:exynos-ehci");
34129824c16SJingoo Han MODULE_AUTHOR("Jingoo Han");
34229824c16SJingoo Han MODULE_AUTHOR("Joonyoung Shim");
34329824c16SJingoo Han MODULE_LICENSE("GPL v2");
344