upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / usb / host / ohci-s5pv210.c
1 /* ohci-s5pv210.c - Driver for USB HOST on Samsung S5PV210 processor
2  *
3  * Bus Glue for SAMSUNG S5PV210 USB HOST OHCI Controller
4  *
5  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
6  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
7  * (C) Copyright 2002 Hewlett-Packard Company
8  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
9  * Author: Jingoo Han <jg1.han@samsung.com>
10  *
11  * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org>
12  * Modified for SAMSUNG s5pv210 OHCI by Jingoo Han <jg1.han@samsung.com>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2 as
16  * published by the Free Software Foundation.
17 */
18
19 #include <linux/platform_device.h>
20
21 extern int usb_disabled(void);
22
23 extern void usb_host_phy_init(void);
24 extern void usb_host_phy_off(void);
25
26 static void s5pv210_start_ohc(void);
27 static void s5pv210_stop_ohc(void);
28 static int ohci_hcd_s5pv210_drv_probe(struct platform_device *pdev);
29 static int ohci_hcd_s5pv210_drv_remove(struct platform_device *pdev);
30
31 #ifdef CONFIG_PM
32 static int ohci_hcd_s5pv210_drv_suspend(
33         struct platform_device *pdev,
34         pm_message_t message
35 ){
36         struct usb_hcd *hcd = platform_get_drvdata(pdev);
37         struct ohci_hcd *ohci = hcd_to_ohci(hcd);
38         unsigned long flags;
39         int rc = 0;
40
41         /* Root hub was already suspended. Disable irq emission and
42          * mark HW unaccessible, bail out if RH has been resumed. Use
43          * the spinlock to properly synchronize with possible pending
44          * RH suspend or resume activity.
45          *
46          * This is still racy as hcd->state is manipulated outside of
47          * any locks =P But that will be a different fix.
48          */
49         spin_lock_irqsave(&ohci->lock, flags);
50         if (hcd->state != HC_STATE_SUSPENDED) {
51                 rc = -EINVAL;
52                 goto bail;
53         }
54
55         /* make sure snapshot being resumed re-enumerates everything */
56         if (message.event == PM_EVENT_PRETHAW)
57                 ohci_usb_reset(ohci);
58
59         clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
60
61         s5pv210_stop_ohc();
62 bail:
63         spin_unlock_irqrestore(&ohci->lock, flags);
64
65         return rc;
66 }
67 static int ohci_hcd_s5pv210_drv_resume(struct platform_device *pdev)
68 {
69         struct usb_hcd *hcd = platform_get_drvdata(pdev);
70         int rc = 0;
71
72         s5pv210_start_ohc();
73
74         /* Mark hardware accessible again as we are out of D3 state by now */
75         set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
76
77         ohci_finish_controller_resume(hcd);
78
79         return rc;
80 }
81 #else
82 #define ohci_hcd_s5pv210_drv_suspend NULL
83 #define ohci_hcd_s5pv210_drv_resume NULL
84 #endif
85
86
87 static void s5pv210_start_ohc(void)
88 {
89         usb_host_phy_init();
90 }
91
92 static void s5pv210_stop_ohc(void)
93 {
94         usb_host_phy_off();
95 }
96
97 static int __devinit ohci_s5pv210_start(struct usb_hcd *hcd)
98 {
99         struct ohci_hcd *ohci = hcd_to_ohci(hcd);
100         int ret;
101
102         ohci_dbg(ohci, "ohci_s5pv210_start, ohci:%p", ohci);
103
104         ret = ohci_init(ohci);
105         if (ret < 0)
106                 return ret;
107
108         ret = ohci_run(ohci);
109         if (ret < 0) {
110                 err("can't start %s", hcd->self.bus_name);
111                 ohci_stop(hcd);
112                 return ret;
113         }
114
115         return 0;
116 }
117
118 static const struct hc_driver ohci_s5pv210_hc_driver = {
119         .description            = hcd_name,
120         .product_desc           = "s5pv210 OHCI",
121         .hcd_priv_size          = sizeof(struct ohci_hcd),
122
123         .irq                    = ohci_irq,
124         .flags                  = HCD_MEMORY|HCD_USB11,
125
126         .start                  = ohci_s5pv210_start,
127         .stop                   = ohci_stop,
128         .shutdown               = ohci_shutdown,
129
130         .get_frame_number       = ohci_get_frame,
131
132         .urb_enqueue            = ohci_urb_enqueue,
133         .urb_dequeue            = ohci_urb_dequeue,
134         .endpoint_disable       = ohci_endpoint_disable,
135
136         .hub_status_data        = ohci_hub_status_data,
137         .hub_control            = ohci_hub_control,
138 #ifdef  CONFIG_PM
139         .bus_suspend            = ohci_bus_suspend,
140         .bus_resume             = ohci_bus_resume,
141 #endif
142         .start_port_reset       = ohci_start_port_reset,
143 };
144
145 static int ohci_hcd_s5pv210_drv_probe(struct platform_device *pdev)
146 {
147         struct usb_hcd  *hcd = NULL;
148         int retval = 0;
149
150         if (usb_disabled())
151                 return -ENODEV;
152
153         if (pdev->resource[1].flags != IORESOURCE_IRQ) {
154                 dev_err(&pdev->dev, "resource[1] is not IORESOURCE_IRQ.\n");
155                 return -ENODEV;
156         }
157
158         hcd = usb_create_hcd(&ohci_s5pv210_hc_driver, &pdev->dev, "s5pv210");
159         if (!hcd) {
160                 dev_err(&pdev->dev, "usb_create_hcd failed!\n");
161                 return -ENODEV;
162         }
163
164         hcd->rsrc_start = pdev->resource[0].start;
165         hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
166
167         if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
168                 dev_err(&pdev->dev, "request_mem_region failed!\n");
169                 retval = -EBUSY;
170                 goto err1;
171         }
172
173         s5pv210_start_ohc();
174
175         hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
176         if (!hcd->regs) {
177                 dev_err(&pdev->dev, "ioremap failed!\n");
178                 retval = -ENOMEM;
179                 goto err2;
180         }
181
182         ohci_hcd_init(hcd_to_ohci(hcd));
183
184         retval = usb_add_hcd(hcd, pdev->resource[1].start,
185                                 IRQF_DISABLED | IRQF_SHARED);
186
187         if (retval == 0) {
188                 platform_set_drvdata(pdev, hcd);
189                 return retval;
190         }
191
192         s5pv210_stop_ohc();
193         iounmap(hcd->regs);
194 err2:
195         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
196 err1:
197         usb_put_hcd(hcd);
198         return retval;
199 }
200
201 static int ohci_hcd_s5pv210_drv_remove(struct platform_device *pdev)
202 {
203         struct usb_hcd *hcd = platform_get_drvdata(pdev);
204
205         usb_remove_hcd(hcd);
206         iounmap(hcd->regs);
207         release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
208         usb_put_hcd(hcd);
209         s5pv210_stop_ohc();
210         platform_set_drvdata(pdev, NULL);
211
212         return 0;
213 }
214
215 static struct platform_driver  ohci_hcd_s5pv210_driver = {
216         .probe          = ohci_hcd_s5pv210_drv_probe,
217         .remove         = ohci_hcd_s5pv210_drv_remove,
218         .shutdown       = usb_hcd_platform_shutdown,
219         .suspend        = ohci_hcd_s5pv210_drv_suspend,
220         .resume         = ohci_hcd_s5pv210_drv_resume,
221         .driver = {
222                 .name = "s5p-ohci",
223                 .owner = THIS_MODULE,
224         }
225 };
226
227 MODULE_ALIAS("platform:s5p-ohci");