Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * MIPS CI13320A EHCI Host Controller driver
  3 * Based on "ehci-au1xxx.c" by K.Boge <karsten.boge@amd.com>
  4 *
  5 * Copyright (C) 2012 MIPS Technologies, Inc.
  6 *
  7 * This program is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU General Public License as published by the
  9 * Free Software Foundation; either version 2 of the License, or (at your
 10 * option) any later version.
 11 *
 12 * This program is distributed in the hope that it will be useful, but
 13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 14 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 15 * for more details.
 16 *
 17 * You should have received a copy of the GNU General Public License
 18 * along with this program; if not, write to the Free Software Foundation,
 19 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 20 */
 21
 22#include <linux/platform_device.h>
 23
 24static int ehci_sead3_setup(struct usb_hcd *hcd)
 25{
 26	int ret;
 27	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 28
 29	ehci->caps = hcd->regs + 0x100;
 30
 31#ifdef __BIG_ENDIAN
 32	ehci->big_endian_mmio = 1;
 33	ehci->big_endian_desc = 1;
 34#endif
 35
 36	ret = ehci_setup(hcd);
 37	if (ret)
 38		return ret;
 39
 40	ehci->need_io_watchdog = 0;
 41
 42	/* Set burst length to 16 words. */
 43	ehci_writel(ehci, 0x1010, &ehci->regs->reserved[1]);
 44
 45	return ret;
 46}
 47
 48const struct hc_driver ehci_sead3_hc_driver = {
 49	.description		= hcd_name,
 50	.product_desc		= "SEAD-3 EHCI",
 51	.hcd_priv_size		= sizeof(struct ehci_hcd),
 52
 53	/*
 54	 * generic hardware linkage
 55	 */
 56	.irq			= ehci_irq,
 57	.flags			= HCD_MEMORY | HCD_USB2,
 58
 59	/*
 60	 * basic lifecycle operations
 61	 *
 62	 */
 63	.reset			= ehci_sead3_setup,
 64	.start			= ehci_run,
 65	.stop			= ehci_stop,
 66	.shutdown		= ehci_shutdown,
 67
 68	/*
 69	 * managing i/o requests and associated device resources
 70	 */
 71	.urb_enqueue		= ehci_urb_enqueue,
 72	.urb_dequeue		= ehci_urb_dequeue,
 73	.endpoint_disable	= ehci_endpoint_disable,
 74	.endpoint_reset		= ehci_endpoint_reset,
 75
 76	/*
 77	 * scheduling support
 78	 */
 79	.get_frame_number	= ehci_get_frame,
 80
 81	/*
 82	 * root hub support
 83	 */
 84	.hub_status_data	= ehci_hub_status_data,
 85	.hub_control		= ehci_hub_control,
 86	.bus_suspend		= ehci_bus_suspend,
 87	.bus_resume		= ehci_bus_resume,
 88	.relinquish_port	= ehci_relinquish_port,
 89	.port_handed_over	= ehci_port_handed_over,
 90
 91	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
 92};
 93
 94static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev)
 95{
 96	struct usb_hcd *hcd;
 97	struct resource *res;
 98	int ret;
 99
100	if (usb_disabled())
101		return -ENODEV;
102
103	if (pdev->resource[1].flags != IORESOURCE_IRQ) {
104		pr_debug("resource[1] is not IORESOURCE_IRQ");
105		return -ENOMEM;
106	}
107	hcd = usb_create_hcd(&ehci_sead3_hc_driver, &pdev->dev, "SEAD-3");
108	if (!hcd)
109		return -ENOMEM;
110
111	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
112	hcd->rsrc_start = res->start;
113	hcd->rsrc_len = resource_size(res);
114
115	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
116		pr_debug("request_mem_region failed");
117		ret = -EBUSY;
118		goto err1;
119	}
120
121	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
122	if (!hcd->regs) {
123		pr_debug("ioremap failed");
124		ret = -ENOMEM;
125		goto err2;
126	}
127
128	/* Root hub has integrated TT. */
129	hcd->has_tt = 1;
130
131	ret = usb_add_hcd(hcd, pdev->resource[1].start,
132			  IRQF_SHARED);
133	if (ret == 0) {
134		platform_set_drvdata(pdev, hcd);
135		return ret;
136	}
137
138	iounmap(hcd->regs);
139err2:
140	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
141err1:
142	usb_put_hcd(hcd);
143	return ret;
144}
145
146static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
147{
148	struct usb_hcd *hcd = platform_get_drvdata(pdev);
149
150	usb_remove_hcd(hcd);
151	iounmap(hcd->regs);
152	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
153	usb_put_hcd(hcd);
154	platform_set_drvdata(pdev, NULL);
155
156	return 0;
157}
158
159#ifdef CONFIG_PM
160static int ehci_hcd_sead3_drv_suspend(struct device *dev)
161{
162	struct usb_hcd *hcd = dev_get_drvdata(dev);
163	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
164	unsigned long flags;
165	int rc = 0;
166
167	if (time_before(jiffies, ehci->next_statechange))
168		msleep(20);
169
170	/* Root hub was already suspended. Disable irq emission and
171	 * mark HW unaccessible.  The PM and USB cores make sure that
172	 * the root hub is either suspended or stopped.
173	 */
174	ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
175	spin_lock_irqsave(&ehci->lock, flags);
176	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
177	(void)ehci_readl(ehci, &ehci->regs->intr_enable);
178
179	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
180	spin_unlock_irqrestore(&ehci->lock, flags);
181
182	/* could save FLADJ in case of Vaux power loss
183	 * ... we'd only use it to handle clock skew
184	 */
185
186	return rc;
187}
188
189static int ehci_hcd_sead3_drv_resume(struct device *dev)
190{
191	struct usb_hcd *hcd = dev_get_drvdata(dev);
192	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
193
194	/* maybe restore FLADJ. */
195
196	if (time_before(jiffies, ehci->next_statechange))
197		msleep(100);
198
199	/* Mark hardware accessible again as we are out of D3 state by now */
200	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
201
202	/* If CF is still set, we maintained PCI Vaux power.
203	 * Just undo the effect of ehci_pci_suspend().
204	 */
205	if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
206		int	mask = INTR_MASK;
207
208		ehci_prepare_ports_for_controller_resume(ehci);
209		if (!hcd->self.root_hub->do_remote_wakeup)
210			mask &= ~STS_PCD;
211		ehci_writel(ehci, mask, &ehci->regs->intr_enable);
212		ehci_readl(ehci, &ehci->regs->intr_enable);
213		return 0;
214	}
215
216	ehci_dbg(ehci, "lost power, restarting\n");
217	usb_root_hub_lost_power(hcd->self.root_hub);
218
219	/* Else reset, to cope with power loss or flush-to-storage
220	 * style "resume" having let BIOS kick in during reboot.
221	 */
222	(void) ehci_halt(ehci);
223	(void) ehci_reset(ehci);
224
225	/* emptying the schedule aborts any urbs */
226	spin_lock_irq(&ehci->lock);
227	if (ehci->reclaim)
228		end_unlink_async(ehci);
229	ehci_work(ehci);
230	spin_unlock_irq(&ehci->lock);
231
232	ehci_writel(ehci, ehci->command, &ehci->regs->command);
233	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
234	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
235
236	/* here we "know" root ports should always stay powered */
237	ehci_port_power(ehci, 1);
238
239	ehci->rh_state = EHCI_RH_SUSPENDED;
240
241	return 0;
242}
243
244static const struct dev_pm_ops sead3_ehci_pmops = {
245	.suspend	= ehci_hcd_sead3_drv_suspend,
246	.resume		= ehci_hcd_sead3_drv_resume,
247};
248
249#define SEAD3_EHCI_PMOPS (&sead3_ehci_pmops)
250
251#else
252#define SEAD3_EHCI_PMOPS NULL
253#endif
254
255static struct platform_driver ehci_hcd_sead3_driver = {
256	.probe		= ehci_hcd_sead3_drv_probe,
257	.remove		= ehci_hcd_sead3_drv_remove,
258	.shutdown	= usb_hcd_platform_shutdown,
259	.driver = {
260		.name	= "sead3-ehci",
261		.owner	= THIS_MODULE,
262		.pm	= SEAD3_EHCI_PMOPS,
263	}
264};
265
266MODULE_ALIAS("platform:sead3-ehci");