xref: /openbmc/linux/drivers/usb/host/ehci-sh.c (revision 20e2fc42)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * SuperH EHCI host controller driver
4  *
5  * Copyright (C) 2010  Paul Mundt
6  *
7  * Based on ohci-sh.c and ehci-atmel.c.
8  */
9 #include <linux/platform_device.h>
10 #include <linux/clk.h>
11 #include <linux/platform_data/ehci-sh.h>
12 
13 struct ehci_sh_priv {
14 	struct clk *iclk, *fclk;
15 	struct usb_hcd *hcd;
16 };
17 
18 static int ehci_sh_reset(struct usb_hcd *hcd)
19 {
20 	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
21 
22 	ehci->caps = hcd->regs;
23 
24 	return ehci_setup(hcd);
25 }
26 
27 static const struct hc_driver ehci_sh_hc_driver = {
28 	.description			= hcd_name,
29 	.product_desc			= "SuperH EHCI",
30 	.hcd_priv_size			= sizeof(struct ehci_hcd),
31 
32 	/*
33 	 * generic hardware linkage
34 	 */
35 	.irq				= ehci_irq,
36 	.flags				= HCD_USB2 | HCD_DMA | HCD_MEMORY | HCD_BH,
37 
38 	/*
39 	 * basic lifecycle operations
40 	 */
41 	.reset				= ehci_sh_reset,
42 	.start				= ehci_run,
43 	.stop				= ehci_stop,
44 	.shutdown			= ehci_shutdown,
45 
46 	/*
47 	 * managing i/o requests and associated device resources
48 	 */
49 	.urb_enqueue			= ehci_urb_enqueue,
50 	.urb_dequeue			= ehci_urb_dequeue,
51 	.endpoint_disable		= ehci_endpoint_disable,
52 	.endpoint_reset			= ehci_endpoint_reset,
53 
54 	/*
55 	 * scheduling support
56 	 */
57 	.get_frame_number		= ehci_get_frame,
58 
59 	/*
60 	 * root hub support
61 	 */
62 	.hub_status_data		= ehci_hub_status_data,
63 	.hub_control			= ehci_hub_control,
64 
65 #ifdef CONFIG_PM
66 	.bus_suspend			= ehci_bus_suspend,
67 	.bus_resume			= ehci_bus_resume,
68 #endif
69 
70 	.relinquish_port		= ehci_relinquish_port,
71 	.port_handed_over		= ehci_port_handed_over,
72 	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
73 };
74 
75 static int ehci_hcd_sh_probe(struct platform_device *pdev)
76 {
77 	struct resource *res;
78 	struct ehci_sh_priv *priv;
79 	struct ehci_sh_platdata *pdata;
80 	struct usb_hcd *hcd;
81 	int irq, ret;
82 
83 	if (usb_disabled())
84 		return -ENODEV;
85 
86 	irq = platform_get_irq(pdev, 0);
87 	if (irq <= 0) {
88 		ret = -ENODEV;
89 		goto fail_create_hcd;
90 	}
91 
92 	pdata = dev_get_platdata(&pdev->dev);
93 
94 	/* initialize hcd */
95 	hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
96 			     dev_name(&pdev->dev));
97 	if (!hcd) {
98 		ret = -ENOMEM;
99 		goto fail_create_hcd;
100 	}
101 
102 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
103 	hcd->regs = devm_ioremap_resource(&pdev->dev, res);
104 	if (IS_ERR(hcd->regs)) {
105 		ret = PTR_ERR(hcd->regs);
106 		goto fail_request_resource;
107 	}
108 	hcd->rsrc_start = res->start;
109 	hcd->rsrc_len = resource_size(res);
110 
111 	priv = devm_kzalloc(&pdev->dev, sizeof(struct ehci_sh_priv),
112 			    GFP_KERNEL);
113 	if (!priv) {
114 		ret = -ENOMEM;
115 		goto fail_request_resource;
116 	}
117 
118 	/* These are optional, we don't care if they fail */
119 	priv->fclk = devm_clk_get(&pdev->dev, "usb_fck");
120 	if (IS_ERR(priv->fclk))
121 		priv->fclk = NULL;
122 
123 	priv->iclk = devm_clk_get(&pdev->dev, "usb_ick");
124 	if (IS_ERR(priv->iclk))
125 		priv->iclk = NULL;
126 
127 	clk_enable(priv->fclk);
128 	clk_enable(priv->iclk);
129 
130 	if (pdata && pdata->phy_init)
131 		pdata->phy_init();
132 
133 	ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
134 	if (ret != 0) {
135 		dev_err(&pdev->dev, "Failed to add hcd");
136 		goto fail_add_hcd;
137 	}
138 	device_wakeup_enable(hcd->self.controller);
139 
140 	priv->hcd = hcd;
141 	platform_set_drvdata(pdev, priv);
142 
143 	return ret;
144 
145 fail_add_hcd:
146 	clk_disable(priv->iclk);
147 	clk_disable(priv->fclk);
148 
149 fail_request_resource:
150 	usb_put_hcd(hcd);
151 fail_create_hcd:
152 	dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
153 
154 	return ret;
155 }
156 
157 static int ehci_hcd_sh_remove(struct platform_device *pdev)
158 {
159 	struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
160 	struct usb_hcd *hcd = priv->hcd;
161 
162 	usb_remove_hcd(hcd);
163 	usb_put_hcd(hcd);
164 
165 	clk_disable(priv->fclk);
166 	clk_disable(priv->iclk);
167 
168 	return 0;
169 }
170 
171 static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
172 {
173 	struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
174 	struct usb_hcd *hcd = priv->hcd;
175 
176 	if (hcd->driver->shutdown)
177 		hcd->driver->shutdown(hcd);
178 }
179 
180 static struct platform_driver ehci_hcd_sh_driver = {
181 	.probe		= ehci_hcd_sh_probe,
182 	.remove		= ehci_hcd_sh_remove,
183 	.shutdown	= ehci_hcd_sh_shutdown,
184 	.driver		= {
185 		.name	= "sh_ehci",
186 	},
187 };
188 
189 MODULE_ALIAS("platform:sh_ehci");
190