Blame view

drivers/usb/host/ohci-au1xxx.c 7.36 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   * OHCI HCD (Host Controller Driver) for USB.
   *
   * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
   * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
   * (C) Copyright 2002 Hewlett-Packard Company
   *
   * Bus Glue for AMD Alchemy Au1xxx
   *
   * Written by Christopher Hoover <ch@hpl.hp.com>
ac310bb5d   Uwe Kleine-König   Fix name of Russe...
11
   * Based on fragments of previous driver by Russell King et al.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
   *
   * Modified for LH7A404 from ohci-sa1111.c
   *  by Durgesh Pattamatta <pattamattad@sharpsec.com>
   * Modified for AMD Alchemy Au1xxx
   *  by Matt Porter <mporter@kernel.crashing.org>
   *
   * This file is licenced under the GPL.
   */
d052d1bef   Russell King   Create platform_d...
20
  #include <linux/platform_device.h>
de25968cc   Tim Schmielau   [PATCH] fix more ...
21
  #include <linux/signal.h>
d052d1bef   Russell King   Create platform_d...
22

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  #include <asm/mach-au1x00/au1000.h>
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
24
  #ifndef	CONFIG_SOC_AU1200
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
32
33
  #define USBH_ENABLE_BE (1<<0)
  #define USBH_ENABLE_C  (1<<1)
  #define USBH_ENABLE_E  (1<<2)
  #define USBH_ENABLE_CE (1<<3)
  #define USBH_ENABLE_RD (1<<4)
  
  #ifdef __LITTLE_ENDIAN
  #define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
  #elif __BIG_ENDIAN
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
34
35
  #define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \
  			  USBH_ENABLE_BE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
  #else
  #error not byte order defined
  #endif
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
39
40
41
42
43
44
45
  #else   /* Au1200 */
  
  #define USB_HOST_CONFIG    (USB_MSR_BASE + USB_MSR_MCFG)
  #define USB_MCFG_PFEN     (1<<31)
  #define USB_MCFG_RDCOMB   (1<<30)
  #define USB_MCFG_SSDEN    (1<<23)
  #define USB_MCFG_OHCCLKEN (1<<16)
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
46
  #ifdef CONFIG_DMA_COHERENT
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
47
  #define USB_MCFG_UCAM     (1<<7)
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
48
49
50
  #else
  #define USB_MCFG_UCAM     (0)
  #endif
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
51
52
53
54
  #define USB_MCFG_OBMEN    (1<<1)
  #define USB_MCFG_OMEMEN   (1<<0)
  
  #define USBH_ENABLE_CE    USB_MCFG_OHCCLKEN
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
55
56
57
58
59
  
  #define USBH_ENABLE_INIT  (USB_MCFG_PFEN  | USB_MCFG_RDCOMB 	|	\
  			   USBH_ENABLE_CE | USB_MCFG_SSDEN	|	\
  			   USB_MCFG_UCAM  |				\
  			   USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
60
61
62
  #define USBH_DISABLE      (USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
  
  #endif  /* Au1200 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  extern int usb_disabled(void);
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
64
  static void au1xxx_start_ohc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  	/* enable host controller */
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
67
  #ifndef CONFIG_SOC_AU1200
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  	au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
69
  	au_sync();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  	udelay(1000);
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
71
72
  	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
  	au_sync();
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
73
  	udelay(1000);
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
74

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
  	/* wait for reset complete (read register twice; see au1500 errata) */
  	while (au_readl(USB_HOST_CONFIG),
  		!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
  		udelay(1000);
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
79
  #else   /* Au1200 */
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
80
81
  	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
  	au_sync();
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
82
  	udelay(1000);
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
83
84
85
86
  
  	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
  	au_sync();
  	udelay(2000);
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
87
  #endif  /* Au1200 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  }
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
89
  static void au1xxx_stop_ohc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  {
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
91
92
93
94
95
  #ifdef CONFIG_SOC_AU1200
  	/* Disable mem */
  	au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
  	au_sync();
  	udelay(1000);
d5fb7f1b5   Jordan Crouse   [PATCH] USB: OHCI...
96
  #endif
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
97
98
99
  	/* Disable clock */
  	au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
  	au_sync();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  }
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
101
  static int __devinit ohci_au1xxx_start(struct usb_hcd *hcd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  {
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
103
104
  	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105

53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
106
  	ohci_dbg(ohci, "ohci_au1xxx_start, ohci:%p", ohci);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107

53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
108
  	if ((ret = ohci_init(ohci)) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
  		return ret;
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
110
  	if ((ret = ohci_run(ohci)) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  		err ("can't start %s", hcd->self.bus_name);
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
112
  		ohci_stop(hcd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
  		return ret;
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  static const struct hc_driver ohci_au1xxx_hc_driver = {
  	.description =		hcd_name,
  	.product_desc =		"Au1xxx OHCI",
  	.hcd_priv_size =	sizeof(struct ohci_hcd),
  
  	/*
  	 * generic hardware linkage
  	 */
  	.irq =			ohci_irq,
  	.flags =		HCD_USB11 | HCD_MEMORY,
  
  	/*
  	 * basic lifecycle operations
  	 */
  	.start =		ohci_au1xxx_start,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  	.stop =			ohci_stop,
dd9048af4   David Brownell   USB: ohci whitesp...
134
  	.shutdown =		ohci_shutdown,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  
  	/*
  	 * managing i/o requests and associated device resources
  	 */
  	.urb_enqueue =		ohci_urb_enqueue,
  	.urb_dequeue =		ohci_urb_dequeue,
  	.endpoint_disable =	ohci_endpoint_disable,
  
  	/*
  	 * scheduling support
  	 */
  	.get_frame_number =	ohci_get_frame,
  
  	/*
  	 * root hub support
  	 */
  	.hub_status_data =	ohci_hub_status_data,
  	.hub_control =		ohci_hub_control,
9293677af   David Brownell   [PATCH] all HCDs ...
153
  #ifdef	CONFIG_PM
0c0382e32   Alan Stern   [PATCH] USB: Rena...
154
155
  	.bus_suspend =		ohci_bus_suspend,
  	.bus_resume =		ohci_bus_resume,
9293677af   David Brownell   [PATCH] all HCDs ...
156
157
  #endif
  	.start_port_reset =	ohci_start_port_reset,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  };
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
159
  static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  	int ret;
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
162
  	struct usb_hcd *hcd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
  
  	if (usb_disabled())
  		return -ENODEV;
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  #if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
  	/* Au1200 AB USB does not support coherent memory */
  	if (!(read_c0_prid() & 0xff)) {
  		printk(KERN_INFO "%s: this is chip revision AB !!
  ",
  			pdev->name);
  		printk(KERN_INFO "%s: update your board or re-configure "
  				 "the kernel
  ", pdev->name);
  		return -ENODEV;
  	}
  #endif
  
  	if (pdev->resource[1].flags != IORESOURCE_IRQ) {
  		pr_debug("resource[1] is not IORESOURCE_IRQ
  ");
  		return -ENOMEM;
  	}
  
  	hcd = usb_create_hcd(&ohci_au1xxx_hc_driver, &pdev->dev, "au1xxx");
  	if (!hcd)
  		return -ENOMEM;
  
  	hcd->rsrc_start = pdev->resource[0].start;
  	hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
  
  	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
  		pr_debug("request_mem_region failed
  ");
  		ret = -EBUSY;
  		goto err1;
  	}
  
  	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
  	if (!hcd->regs) {
  		pr_debug("ioremap failed
  ");
  		ret = -ENOMEM;
  		goto err2;
  	}
  
  	au1xxx_start_ohc();
  	ohci_hcd_init(hcd_to_ohci(hcd));
  
  	ret = usb_add_hcd(hcd, pdev->resource[1].start,
  			  IRQF_DISABLED | IRQF_SHARED);
  	if (ret == 0) {
  		platform_set_drvdata(pdev, hcd);
  		return ret;
  	}
  
  	au1xxx_stop_ohc();
  	iounmap(hcd->regs);
  err2:
  	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
  err1:
  	usb_put_hcd(hcd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  	return ret;
  }
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
225
  static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  {
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
227
  	struct usb_hcd *hcd = platform_get_drvdata(pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228

53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
229
230
231
232
233
234
  	usb_remove_hcd(hcd);
  	au1xxx_stop_ohc();
  	iounmap(hcd->regs);
  	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
  	usb_put_hcd(hcd);
  	platform_set_drvdata(pdev, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
  	return 0;
  }
53c81a348   Manuel Lauss   USB: Au1xxx-usb: ...
237

42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
238
  #ifdef CONFIG_PM
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
239
  static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  {
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
241
  	struct usb_hcd *hcd = dev_get_drvdata(dev);
42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
  	unsigned long flags;
  	int rc;
  
  	rc = 0;
  
  	/* Root hub was already suspended. Disable irq emission and
  	 * mark HW unaccessible, bail out if RH has been resumed. Use
  	 * the spinlock to properly synchronize with possible pending
  	 * RH suspend or resume activity.
  	 *
  	 * This is still racy as hcd->state is manipulated outside of
  	 * any locks =P But that will be a different fix.
  	 */
  	spin_lock_irqsave(&ohci->lock, flags);
  	if (hcd->state != HC_STATE_SUSPENDED) {
  		rc = -EINVAL;
  		goto bail;
  	}
  	ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
  	(void)ohci_readl(ohci, &ohci->regs->intrdisable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263

42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
264
265
266
267
268
269
270
  	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
  
  	au1xxx_stop_ohc();
  bail:
  	spin_unlock_irqrestore(&ohci->lock, flags);
  
  	return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  }
42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
272

807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
273
  static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  {
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
275
  	struct usb_hcd *hcd = dev_get_drvdata(dev);
42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
276
277
278
279
280
  
  	au1xxx_start_ohc();
  
  	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
  	ohci_finish_controller_resume(hcd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
  
  	return 0;
  }
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
284

471452104   Alexey Dobriyan   const: constify r...
285
  static const struct dev_pm_ops au1xxx_ohci_pmops = {
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
286
287
288
289
290
  	.suspend	= ohci_hcd_au1xxx_drv_suspend,
  	.resume		= ohci_hcd_au1xxx_drv_resume,
  };
  
  #define AU1XXX_OHCI_PMOPS &au1xxx_ohci_pmops
42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
291
  #else
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
292
  #define AU1XXX_OHCI_PMOPS NULL
42bfc7b44   Manuel Lauss   USB: Au1xxx-usb: ...
293
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294

3ae5eaec1   Russell King   [DRIVER MODEL] Co...
295
  static struct platform_driver ohci_hcd_au1xxx_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
  	.probe		= ohci_hcd_au1xxx_drv_probe,
  	.remove		= ohci_hcd_au1xxx_drv_remove,
dd9048af4   David Brownell   USB: ohci whitesp...
298
  	.shutdown	= usb_hcd_platform_shutdown,
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
299
300
301
  	.driver		= {
  		.name	= "au1xxx-ohci",
  		.owner	= THIS_MODULE,
807fcb5e1   Manuel Lauss   USB: au1xxx: add ...
302
  		.pm	= AU1XXX_OHCI_PMOPS,
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
303
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  };
f4fce61d4   Kay Sievers   usb host: fix pla...
305
  MODULE_ALIAS("platform:au1xxx-ohci");