xref: /linux/drivers/usb/chipidea/ci_hdrc_tegra.c (revision 12c3de7ef7420de6974bbffc4f7d321577aad2d4)
1 /*
2  * Copyright (c) 2016, NVIDIA Corporation
3  */
4 
5 #include <linux/clk.h>
6 #include <linux/module.h>
7 #include <linux/of_device.h>
8 #include <linux/reset.h>
9 
10 #include <linux/usb/chipidea.h>
11 
12 #include "ci.h"
13 
14 struct tegra_udc {
15 	struct ci_hdrc_platform_data data;
16 	struct platform_device *dev;
17 
18 	struct usb_phy *phy;
19 	struct clk *clk;
20 };
21 
22 struct tegra_udc_soc_info {
23 	unsigned long flags;
24 };
25 
26 static const struct tegra_udc_soc_info tegra20_udc_soc_info = {
27 	.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
28 };
29 
30 static const struct tegra_udc_soc_info tegra30_udc_soc_info = {
31 	.flags = 0,
32 };
33 
34 static const struct tegra_udc_soc_info tegra114_udc_soc_info = {
35 	.flags = 0,
36 };
37 
38 static const struct tegra_udc_soc_info tegra124_udc_soc_info = {
39 	.flags = 0,
40 };
41 
42 static const struct of_device_id tegra_udc_of_match[] = {
43 	{
44 		.compatible = "nvidia,tegra20-udc",
45 		.data = &tegra20_udc_soc_info,
46 	}, {
47 		.compatible = "nvidia,tegra30-udc",
48 		.data = &tegra30_udc_soc_info,
49 	}, {
50 		.compatible = "nvidia,tegra114-udc",
51 		.data = &tegra114_udc_soc_info,
52 	}, {
53 		.compatible = "nvidia,tegra124-udc",
54 		.data = &tegra124_udc_soc_info,
55 	}, {
56 		/* sentinel */
57 	}
58 };
59 MODULE_DEVICE_TABLE(of, tegra_udc_of_match);
60 
61 static int tegra_udc_probe(struct platform_device *pdev)
62 {
63 	const struct tegra_udc_soc_info *soc;
64 	struct tegra_udc *udc;
65 	int err;
66 
67 	udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
68 	if (!udc)
69 		return -ENOMEM;
70 
71 	soc = of_device_get_match_data(&pdev->dev);
72 	if (!soc) {
73 		dev_err(&pdev->dev, "failed to match OF data\n");
74 		return -EINVAL;
75 	}
76 
77 	udc->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
78 	if (IS_ERR(udc->phy)) {
79 		err = PTR_ERR(udc->phy);
80 		dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
81 		return err;
82 	}
83 
84 	udc->clk = devm_clk_get(&pdev->dev, NULL);
85 	if (IS_ERR(udc->clk)) {
86 		err = PTR_ERR(udc->clk);
87 		dev_err(&pdev->dev, "failed to get clock: %d\n", err);
88 		return err;
89 	}
90 
91 	err = clk_prepare_enable(udc->clk);
92 	if (err < 0) {
93 		dev_err(&pdev->dev, "failed to enable clock: %d\n", err);
94 		return err;
95 	}
96 
97 	/*
98 	 * Tegra's USB PHY driver doesn't implement optional phy_init()
99 	 * hook, so we have to power on UDC controller before ChipIdea
100 	 * driver initialization kicks in.
101 	 */
102 	usb_phy_set_suspend(udc->phy, 0);
103 
104 	/* setup and register ChipIdea HDRC device */
105 	udc->data.name = "tegra-udc";
106 	udc->data.flags = soc->flags;
107 	udc->data.usb_phy = udc->phy;
108 	udc->data.capoffset = DEF_CAPOFFSET;
109 
110 	udc->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
111 				      pdev->num_resources, &udc->data);
112 	if (IS_ERR(udc->dev)) {
113 		err = PTR_ERR(udc->dev);
114 		dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err);
115 		goto fail_power_off;
116 	}
117 
118 	platform_set_drvdata(pdev, udc);
119 
120 	return 0;
121 
122 fail_power_off:
123 	usb_phy_set_suspend(udc->phy, 1);
124 	clk_disable_unprepare(udc->clk);
125 	return err;
126 }
127 
128 static int tegra_udc_remove(struct platform_device *pdev)
129 {
130 	struct tegra_udc *udc = platform_get_drvdata(pdev);
131 
132 	usb_phy_set_suspend(udc->phy, 1);
133 	clk_disable_unprepare(udc->clk);
134 
135 	return 0;
136 }
137 
138 static struct platform_driver tegra_udc_driver = {
139 	.driver = {
140 		.name = "tegra-udc",
141 		.of_match_table = tegra_udc_of_match,
142 	},
143 	.probe = tegra_udc_probe,
144 	.remove = tegra_udc_remove,
145 };
146 module_platform_driver(tegra_udc_driver);
147 
148 MODULE_DESCRIPTION("NVIDIA Tegra USB device mode driver");
149 MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
150 MODULE_ALIAS("platform:tegra-udc");
151 MODULE_LICENSE("GPL v2");
152