Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
  4 * Author: Chao Xie <chao.xie@marvell.com>
  5 *        Neil Zhang <zhangwm@marvell.com>
  6 */
  7
  8#include <linux/kernel.h>
  9#include <linux/module.h>
 10#include <linux/platform_device.h>
 11#include <linux/clk.h>
 12#include <linux/err.h>
 13#include <linux/usb/otg.h>
 14#include <linux/platform_data/mv_usb.h>
 15
 16#define CAPLENGTH_MASK         (0xff)
 17
 18struct ehci_hcd_mv {
 19	struct usb_hcd *hcd;
 20
 21	/* Which mode does this ehci running OTG/Host ? */
 22	int mode;
 23
 24	void __iomem *phy_regs;
 25	void __iomem *cap_regs;
 26	void __iomem *op_regs;
 27
 28	struct usb_phy *otg;
 29
 30	struct mv_usb_platform_data *pdata;
 31
 32	struct clk *clk;
 33};
 34
 35static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
 36{
 37	clk_prepare_enable(ehci_mv->clk);
 38}
 39
 40static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
 41{
 42	clk_disable_unprepare(ehci_mv->clk);
 43}
 44
 45static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
 46{
 47	int retval;
 48
 49	ehci_clock_enable(ehci_mv);
 50	if (ehci_mv->pdata->phy_init) {
 51		retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
 52		if (retval)
 53			return retval;
 54	}
 55
 56	return 0;
 57}
 58
 59static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
 60{
 61	if (ehci_mv->pdata->phy_deinit)
 62		ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
 63	ehci_clock_disable(ehci_mv);
 64}
 65
 66static int mv_ehci_reset(struct usb_hcd *hcd)
 67{
 68	struct device *dev = hcd->self.controller;
 69	struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
 70	int retval;
 71
 72	if (ehci_mv == NULL) {
 73		dev_err(dev, "Can not find private ehci data\n");
 74		return -ENODEV;
 75	}
 76
 77	hcd->has_tt = 1;
 78
 79	retval = ehci_setup(hcd);
 80	if (retval)
 81		dev_err(dev, "ehci_setup failed %d\n", retval);
 82
 83	return retval;
 84}
 85
 86static const struct hc_driver mv_ehci_hc_driver = {
 87	.description = hcd_name,
 88	.product_desc = "Marvell EHCI",
 89	.hcd_priv_size = sizeof(struct ehci_hcd),
 90
 91	/*
 92	 * generic hardware linkage
 93	 */
 94	.irq = ehci_irq,
 95	.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 96
 97	/*
 98	 * basic lifecycle operations
 99	 */
100	.reset = mv_ehci_reset,
101	.start = ehci_run,
102	.stop = ehci_stop,
103	.shutdown = ehci_shutdown,
104
105	/*
106	 * managing i/o requests and associated device resources
107	 */
108	.urb_enqueue = ehci_urb_enqueue,
109	.urb_dequeue = ehci_urb_dequeue,
110	.endpoint_disable = ehci_endpoint_disable,
111	.endpoint_reset = ehci_endpoint_reset,
112	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
113
114	/*
115	 * scheduling support
116	 */
117	.get_frame_number = ehci_get_frame,
118
119	/*
120	 * root hub support
121	 */
122	.hub_status_data = ehci_hub_status_data,
123	.hub_control = ehci_hub_control,
124	.bus_suspend = ehci_bus_suspend,
125	.bus_resume = ehci_bus_resume,
126};
127
128static int mv_ehci_probe(struct platform_device *pdev)
129{
130	struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
131	struct usb_hcd *hcd;
132	struct ehci_hcd *ehci;
133	struct ehci_hcd_mv *ehci_mv;
134	struct resource *r;
135	int retval = -ENODEV;
136	u32 offset;
137
138	if (!pdata) {
139		dev_err(&pdev->dev, "missing platform_data\n");
140		return -ENODEV;
141	}
142
143	if (usb_disabled())
144		return -ENODEV;
145
146	hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
147	if (!hcd)
148		return -ENOMEM;
149
150	ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL);
151	if (ehci_mv == NULL) {
152		retval = -ENOMEM;
153		goto err_put_hcd;
154	}
155
156	platform_set_drvdata(pdev, ehci_mv);
157	ehci_mv->pdata = pdata;
158	ehci_mv->hcd = hcd;
159
160	ehci_mv->clk = devm_clk_get(&pdev->dev, NULL);
161	if (IS_ERR(ehci_mv->clk)) {
162		dev_err(&pdev->dev, "error getting clock\n");
163		retval = PTR_ERR(ehci_mv->clk);
164		goto err_put_hcd;
165	}
166
167	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
168	ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r);
169	if (IS_ERR(ehci_mv->phy_regs)) {
170		retval = PTR_ERR(ehci_mv->phy_regs);
171		goto err_put_hcd;
172	}
173
174	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
175	ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r);
176	if (IS_ERR(ehci_mv->cap_regs)) {
177		retval = PTR_ERR(ehci_mv->cap_regs);
178		goto err_put_hcd;
179	}
180
181	retval = mv_ehci_enable(ehci_mv);
182	if (retval) {
183		dev_err(&pdev->dev, "init phy error %d\n", retval);
184		goto err_put_hcd;
185	}
186
187	offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
188	ehci_mv->op_regs =
189		(void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
190
191	hcd->rsrc_start = r->start;
192	hcd->rsrc_len = resource_size(r);
193	hcd->regs = ehci_mv->op_regs;
194
195	hcd->irq = platform_get_irq(pdev, 0);
196	if (!hcd->irq) {
197		dev_err(&pdev->dev, "Cannot get irq.");
198		retval = -ENODEV;
199		goto err_disable_clk;
200	}
201
202	ehci = hcd_to_ehci(hcd);
203	ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
204
205	ehci_mv->mode = pdata->mode;
206	if (ehci_mv->mode == MV_USB_MODE_OTG) {
207		ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
208		if (IS_ERR(ehci_mv->otg)) {
209			retval = PTR_ERR(ehci_mv->otg);
210
211			if (retval == -ENXIO)
212				dev_info(&pdev->dev, "MV_USB_MODE_OTG "
213						"must have CONFIG_USB_PHY enabled\n");
214			else
215				dev_err(&pdev->dev,
216						"unable to find transceiver\n");
217			goto err_disable_clk;
218		}
219
220		retval = otg_set_host(ehci_mv->otg->otg, &hcd->self);
221		if (retval < 0) {
222			dev_err(&pdev->dev,
223				"unable to register with transceiver\n");
224			retval = -ENODEV;
225			goto err_disable_clk;
226		}
227		/* otg will enable clock before use as host */
228		mv_ehci_disable(ehci_mv);
229	} else {
230		if (pdata->set_vbus)
231			pdata->set_vbus(1);
232
233		retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
234		if (retval) {
235			dev_err(&pdev->dev,
236				"failed to add hcd with err %d\n", retval);
237			goto err_set_vbus;
238		}
239		device_wakeup_enable(hcd->self.controller);
240	}
241
242	if (pdata->private_init)
243		pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
244
245	dev_info(&pdev->dev,
246		 "successful find EHCI device with regs 0x%p irq %d"
247		 " working in %s mode\n", hcd->regs, hcd->irq,
248		 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
249
250	return 0;
251
252err_set_vbus:
253	if (pdata->set_vbus)
254		pdata->set_vbus(0);
255err_disable_clk:
256	mv_ehci_disable(ehci_mv);
257err_put_hcd:
258	usb_put_hcd(hcd);
259
260	return retval;
261}
262
263static int mv_ehci_remove(struct platform_device *pdev)
264{
265	struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
266	struct usb_hcd *hcd = ehci_mv->hcd;
267
268	if (hcd->rh_registered)
269		usb_remove_hcd(hcd);
270
271	if (!IS_ERR_OR_NULL(ehci_mv->otg))
272		otg_set_host(ehci_mv->otg->otg, NULL);
273
274	if (ehci_mv->mode == MV_USB_MODE_HOST) {
275		if (ehci_mv->pdata->set_vbus)
276			ehci_mv->pdata->set_vbus(0);
277
278		mv_ehci_disable(ehci_mv);
279	}
280
281	usb_put_hcd(hcd);
282
283	return 0;
284}
285
286MODULE_ALIAS("mv-ehci");
287
288static const struct platform_device_id ehci_id_table[] = {
289	{"pxa-u2oehci", PXA_U2OEHCI},
290	{"pxa-sph", PXA_SPH},
291	{"mmp3-hsic", MMP3_HSIC},
292	{"mmp3-fsic", MMP3_FSIC},
293	{},
294};
295
296static void mv_ehci_shutdown(struct platform_device *pdev)
297{
298	struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
299	struct usb_hcd *hcd = ehci_mv->hcd;
300
301	if (!hcd->rh_registered)
302		return;
303
304	if (hcd->driver->shutdown)
305		hcd->driver->shutdown(hcd);
306}
307
308static struct platform_driver ehci_mv_driver = {
309	.probe = mv_ehci_probe,
310	.remove = mv_ehci_remove,
311	.shutdown = mv_ehci_shutdown,
312	.driver = {
313		   .name = "mv-ehci",
314		   .bus = &platform_bus_type,
315		   },
316	.id_table = ehci_id_table,
317};