• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/usb/host/
1/*
2 * OHCI HCD (Host Controller Driver) for USB.
3 *
4 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
5 * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
6 * (C) Copyright 2002 Hewlett-Packard Company
7 *
8 * Bus Glue for AMD Alchemy Au1xxx
9 *
10 * Written by Christopher Hoover <ch@hpl.hp.com>
11 * Based on fragments of previous driver by Russell King et al.
12 *
13 * Modified for LH7A404 from ohci-sa1111.c
14 *  by Durgesh Pattamatta <pattamattad@sharpsec.com>
15 * Modified for AMD Alchemy Au1xxx
16 *  by Matt Porter <mporter@kernel.crashing.org>
17 *
18 * This file is licenced under the GPL.
19 */
20
21#include <linux/platform_device.h>
22#include <linux/signal.h>
23
24#include <asm/mach-au1x00/au1000.h>
25
26#ifndef	CONFIG_SOC_AU1200
27
28#define USBH_ENABLE_BE (1<<0)
29#define USBH_ENABLE_C  (1<<1)
30#define USBH_ENABLE_E  (1<<2)
31#define USBH_ENABLE_CE (1<<3)
32#define USBH_ENABLE_RD (1<<4)
33
34#ifdef __LITTLE_ENDIAN
35#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
36#elif __BIG_ENDIAN
37#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \
38			  USBH_ENABLE_BE)
39#else
40#error not byte order defined
41#endif
42
43#else   /* Au1200 */
44
45#define USB_HOST_CONFIG    (USB_MSR_BASE + USB_MSR_MCFG)
46#define USB_MCFG_PFEN     (1<<31)
47#define USB_MCFG_RDCOMB   (1<<30)
48#define USB_MCFG_SSDEN    (1<<23)
49#define USB_MCFG_OHCCLKEN (1<<16)
50#ifdef CONFIG_DMA_COHERENT
51#define USB_MCFG_UCAM     (1<<7)
52#else
53#define USB_MCFG_UCAM     (0)
54#endif
55#define USB_MCFG_OBMEN    (1<<1)
56#define USB_MCFG_OMEMEN   (1<<0)
57
58#define USBH_ENABLE_CE    USB_MCFG_OHCCLKEN
59
60#define USBH_ENABLE_INIT  (USB_MCFG_PFEN  | USB_MCFG_RDCOMB 	|	\
61			   USBH_ENABLE_CE | USB_MCFG_SSDEN	|	\
62			   USB_MCFG_UCAM  |				\
63			   USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
64
65#define USBH_DISABLE      (USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
66
67#endif  /* Au1200 */
68
69extern int usb_disabled(void);
70
71static void au1xxx_start_ohc(void)
72{
73	/* enable host controller */
74#ifndef CONFIG_SOC_AU1200
75	au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
76	au_sync();
77	udelay(1000);
78
79	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
80	au_sync();
81	udelay(1000);
82
83	/* wait for reset complete (read register twice; see au1500 errata) */
84	while (au_readl(USB_HOST_CONFIG),
85		!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
86		udelay(1000);
87
88#else   /* Au1200 */
89	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
90	au_sync();
91	udelay(1000);
92
93	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
94	au_sync();
95	udelay(2000);
96#endif  /* Au1200 */
97}
98
99static void au1xxx_stop_ohc(void)
100{
101#ifdef CONFIG_SOC_AU1200
102	/* Disable mem */
103	au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
104	au_sync();
105	udelay(1000);
106#endif
107	/* Disable clock */
108	au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
109	au_sync();
110}
111
112static int __devinit ohci_au1xxx_start(struct usb_hcd *hcd)
113{
114	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
115	int ret;
116
117	ohci_dbg(ohci, "ohci_au1xxx_start, ohci:%p", ohci);
118
119	if ((ret = ohci_init(ohci)) < 0)
120		return ret;
121
122	if ((ret = ohci_run(ohci)) < 0) {
123		err ("can't start %s", hcd->self.bus_name);
124		ohci_stop(hcd);
125		return ret;
126	}
127
128	return 0;
129}
130
131static const struct hc_driver ohci_au1xxx_hc_driver = {
132	.description =		hcd_name,
133	.product_desc =		"Au1xxx OHCI",
134	.hcd_priv_size =	sizeof(struct ohci_hcd),
135
136	/*
137	 * generic hardware linkage
138	 */
139	.irq =			ohci_irq,
140	.flags =		HCD_USB11 | HCD_MEMORY,
141
142	/*
143	 * basic lifecycle operations
144	 */
145	.start =		ohci_au1xxx_start,
146	.stop =			ohci_stop,
147	.shutdown =		ohci_shutdown,
148
149	/*
150	 * managing i/o requests and associated device resources
151	 */
152	.urb_enqueue =		ohci_urb_enqueue,
153	.urb_dequeue =		ohci_urb_dequeue,
154	.endpoint_disable =	ohci_endpoint_disable,
155
156	/*
157	 * scheduling support
158	 */
159	.get_frame_number =	ohci_get_frame,
160
161	/*
162	 * root hub support
163	 */
164	.hub_status_data =	ohci_hub_status_data,
165	.hub_control =		ohci_hub_control,
166#ifdef	CONFIG_PM
167	.bus_suspend =		ohci_bus_suspend,
168	.bus_resume =		ohci_bus_resume,
169#endif
170	.start_port_reset =	ohci_start_port_reset,
171};
172
173static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
174{
175	int ret;
176	struct usb_hcd *hcd;
177
178	if (usb_disabled())
179		return -ENODEV;
180
181#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
182	/* Au1200 AB USB does not support coherent memory */
183	if (!(read_c0_prid() & 0xff)) {
184		printk(KERN_INFO "%s: this is chip revision AB !!\n",
185			pdev->name);
186		printk(KERN_INFO "%s: update your board or re-configure "
187				 "the kernel\n", pdev->name);
188		return -ENODEV;
189	}
190#endif
191
192	if (pdev->resource[1].flags != IORESOURCE_IRQ) {
193		pr_debug("resource[1] is not IORESOURCE_IRQ\n");
194		return -ENOMEM;
195	}
196
197	hcd = usb_create_hcd(&ohci_au1xxx_hc_driver, &pdev->dev, "au1xxx");
198	if (!hcd)
199		return -ENOMEM;
200
201	hcd->rsrc_start = pdev->resource[0].start;
202	hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
203
204	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
205		pr_debug("request_mem_region failed\n");
206		ret = -EBUSY;
207		goto err1;
208	}
209
210	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
211	if (!hcd->regs) {
212		pr_debug("ioremap failed\n");
213		ret = -ENOMEM;
214		goto err2;
215	}
216
217	au1xxx_start_ohc();
218	ohci_hcd_init(hcd_to_ohci(hcd));
219
220	ret = usb_add_hcd(hcd, pdev->resource[1].start,
221			  IRQF_DISABLED | IRQF_SHARED);
222	if (ret == 0) {
223		platform_set_drvdata(pdev, hcd);
224		return ret;
225	}
226
227	au1xxx_stop_ohc();
228	iounmap(hcd->regs);
229err2:
230	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
231err1:
232	usb_put_hcd(hcd);
233	return ret;
234}
235
236static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
237{
238	struct usb_hcd *hcd = platform_get_drvdata(pdev);
239
240	usb_remove_hcd(hcd);
241	au1xxx_stop_ohc();
242	iounmap(hcd->regs);
243	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
244	usb_put_hcd(hcd);
245	platform_set_drvdata(pdev, NULL);
246
247	return 0;
248}
249
250#ifdef CONFIG_PM
251static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
252{
253	struct usb_hcd *hcd = dev_get_drvdata(dev);
254	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
255	unsigned long flags;
256	int rc;
257
258	rc = 0;
259
260	/* Root hub was already suspended. Disable irq emission and
261	 * mark HW unaccessible, bail out if RH has been resumed. Use
262	 * the spinlock to properly synchronize with possible pending
263	 * RH suspend or resume activity.
264	 *
265	 * This is still racy as hcd->state is manipulated outside of
266	 * any locks =P But that will be a different fix.
267	 */
268	spin_lock_irqsave(&ohci->lock, flags);
269	if (hcd->state != HC_STATE_SUSPENDED) {
270		rc = -EINVAL;
271		goto bail;
272	}
273	ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
274	(void)ohci_readl(ohci, &ohci->regs->intrdisable);
275
276	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
277
278	au1xxx_stop_ohc();
279bail:
280	spin_unlock_irqrestore(&ohci->lock, flags);
281
282	return rc;
283}
284
285static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
286{
287	struct usb_hcd *hcd = dev_get_drvdata(dev);
288
289	au1xxx_start_ohc();
290
291	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
292	ohci_finish_controller_resume(hcd);
293
294	return 0;
295}
296
297static const struct dev_pm_ops au1xxx_ohci_pmops = {
298	.suspend	= ohci_hcd_au1xxx_drv_suspend,
299	.resume		= ohci_hcd_au1xxx_drv_resume,
300};
301
302#define AU1XXX_OHCI_PMOPS &au1xxx_ohci_pmops
303
304#else
305#define AU1XXX_OHCI_PMOPS NULL
306#endif
307
308static struct platform_driver ohci_hcd_au1xxx_driver = {
309	.probe		= ohci_hcd_au1xxx_drv_probe,
310	.remove		= ohci_hcd_au1xxx_drv_remove,
311	.shutdown	= usb_hcd_platform_shutdown,
312	.driver		= {
313		.name	= "au1xxx-ohci",
314		.owner	= THIS_MODULE,
315		.pm	= AU1XXX_OHCI_PMOPS,
316	},
317};
318
319MODULE_ALIAS("platform:au1xxx-ohci");
320