Subversion Repositories shark

Rev

Rev 846 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
846 giacomo 1
/*
2
 * (C) Copyright David Brownell 2000-2002
3
 *
4
 * This program is free software; you can redistribute it and/or modify it
5
 * under the terms of the GNU General Public License as published by the
6
 * Free Software Foundation; either version 2 of the License, or (at your
7
 * option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful, but
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12
 * for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
 */
18
 
19
#include <linuxcomp.h>
20
 
21
#include <linux/config.h>
22
 
23
#ifdef CONFIG_USB_DEBUG
24
        #define DEBUG
25
#else
26
        #undef DEBUG
27
#endif
28
 
29
#include <linux/kernel.h>
30
#include <linux/module.h>
31
#include <linux/pci.h>
32
#include <asm/io.h>
33
#include <asm/irq.h>
34
#include <linux/usb.h>
35
#include "hcd.h"
36
 
37
 
38
/* PCI-based HCs are normal, but custom bus glue should be ok */
39
 
40
 
41
/*-------------------------------------------------------------------------*/
42
 
43
static void hcd_pci_release(struct usb_bus *bus)
44
{
45
        struct usb_hcd *hcd = bus->hcpriv;
46
 
47
        if (hcd)
48
                hcd->driver->hcd_free(hcd);
49
}
50
 
51
/* configure so an HC device and id are always provided */
52
/* always called with process context; sleeping is OK */
53
 
54
/**
55
 * usb_hcd_pci_probe - initialize PCI-based HCDs
56
 * @dev: USB Host Controller being probed
57
 * @id: pci hotplug id connecting controller to HCD framework
58
 * Context: !in_interrupt()
59
 *
60
 * Allocates basic PCI resources for this USB host controller, and
61
 * then invokes the start() method for the HCD associated with it
62
 * through the hotplug entry's driver_data.
63
 *
64
 * Store this function in the HCD's struct pci_driver as probe().
65
 */
66
int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
67
{
68
        struct hc_driver        *driver;
69
        unsigned long           resource, len;
70
        void                    *base;
71
        struct usb_hcd          *hcd;
72
        int                     retval, region;
73
        char                    buf [8], *bufp = buf;
74
 
75
        if (usb_disabled())
76
                return -ENODEV;
77
 
78
        if (!id || !(driver = (struct hc_driver *) id->driver_data))
79
                return -EINVAL;
80
 
81
        if (pci_enable_device (dev) < 0)
82
                return -ENODEV;
83
 
84
        if (!dev->irq) {
85
                err ("Found HC with no IRQ.  Check BIOS/PCI %s setup!",
86
                        pci_name(dev));
87
                return -ENODEV;
88
        }
89
 
90
        if (driver->flags & HCD_MEMORY) {       // EHCI, OHCI
91
 
92
                region = 0;
93
                resource = pci_resource_start (dev, 0);
94
                len = pci_resource_len (dev, 0);
95
                if (!request_mem_region (resource, len, driver->description)) {
96
                        dbg ("controller already in use");
97
                        return -EBUSY;
98
                }
99
                base = ioremap_nocache (resource, len);
100
                if (base == NULL) {
101
                        dbg ("error mapping memory");
102
                        retval = -EFAULT;
103
clean_1:
104
                        release_mem_region (resource, len);
105
                        err ("init %s fail, %d", pci_name(dev), retval);
106
                        return retval;
107
                }
108
 
109
        } else {                                // UHCI
110
                resource = len = 0;
111
                for (region = 0; region < PCI_ROM_RESOURCE; region++) {
112
                        if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
113
                                continue;
114
 
115
                        resource = pci_resource_start (dev, region);
116
                        len = pci_resource_len (dev, region);
117
                        if (request_region (resource, len,
118
                                        driver->description))
119
                                break;
120
                }
121
                if (region == PCI_ROM_RESOURCE) {
122
                        dbg ("no i/o regions available");
123
                        return -EBUSY;
124
                }
125
                base = (void *) resource;
126
        }
127
 
128
        // driver->reset(), later on, will transfer device from
129
        // control by SMM/BIOS to control by Linux (if needed)
130
 
131
        hcd = driver->hcd_alloc ();
132
        if (hcd == NULL){
133
                dbg ("hcd alloc fail");
134
                retval = -ENOMEM;
135
clean_2:
136
                if (driver->flags & HCD_MEMORY) {
137
                        iounmap (base);
138
                        goto clean_1;
139
                } else {
140
                        release_region (resource, len);
141
                        err ("init %s fail, %d", pci_name(dev), retval);
142
                        return retval;
143
                }
144
        }
145
 
146
        // hcd zeroed everything
147
        hcd->regs = base;
148
        hcd->region = region;
149
 
150
        pci_set_drvdata (dev, hcd);
151
        hcd->driver = driver;
152
        hcd->description = driver->description;
153
        hcd->pdev = dev;
154
        hcd->self.bus_name = pci_name(dev);
155
        if (hcd->product_desc == NULL)
156
                hcd->product_desc = "USB Host Controller";
157
        hcd->self.controller = &dev->dev;
158
        hcd->controller = hcd->self.controller;
159
 
160
        if ((retval = hcd_buffer_create (hcd)) != 0) {
161
clean_3:
162
                driver->hcd_free (hcd);
163
                goto clean_2;
164
        }
165
 
166
        dev_info (hcd->controller, "%s\n", hcd->product_desc);
167
 
168
        /* till now HC has been in an indeterminate state ... */
169
        if (driver->reset && (retval = driver->reset (hcd)) < 0) {
170
                dev_err (hcd->controller, "can't reset\n");
171
                goto clean_3;
172
        }
173
        hcd->state = USB_STATE_HALT;
174
 
175
        pci_set_master (dev);
176
#ifndef __sparc__
177
        sprintf26 (buf, "%d", dev->irq);
178
#else
179
        bufp = __irq_itoa(dev->irq);
180
#endif
181
 
182
        retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
183
                                hcd->description, hcd);
184
        if (retval != 0) {
185
                dev_err (hcd->controller,
186
                                "request interrupt %s failed\n", bufp);
187
                goto clean_3;
188
        }
189
        hcd->irq = dev->irq;
190
 
191
        dev_info (hcd->controller, "irq %s, %s %p\n", bufp,
192
                (driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
193
                base);
194
        usb_bus_init (&hcd->self);
195
        hcd->self.op = &usb_hcd_operations;
196
        hcd->self.hcpriv = (void *) hcd;
197
        hcd->self.release = &hcd_pci_release;
198
 
199
        INIT_LIST_HEAD (&hcd->dev_list);
200
        usb_register_bus (&hcd->self);
201
 
202
        if ((retval = driver->start (hcd)) < 0)
203
                usb_hcd_pci_remove (dev);
204
 
205
        return retval;
206
}
207
EXPORT_SYMBOL (usb_hcd_pci_probe);
208
 
209
 
210
/* may be called without controller electrically present */
211
/* may be called with controller, bus, and devices active */
212
 
213
/**
214
 * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
215
 * @dev: USB Host Controller being removed
216
 * Context: !in_interrupt()
217
 *
218
 * Reverses the effect of usb_hcd_pci_probe(), first invoking
219
 * the HCD's stop() method.  It is always called from a thread
220
 * context, normally "rmmod", "apmd", or something similar.
221
 *
222
 * Store this function in the HCD's struct pci_driver as remove().
223
 */
224
void usb_hcd_pci_remove (struct pci_dev *dev)
225
{
226
        struct usb_hcd          *hcd;
227
        struct usb_device       *hub;
228
 
229
        hcd = pci_get_drvdata(dev);
230
        if (!hcd)
231
                return;
232
        dev_info (hcd->controller, "remove, state %x\n", hcd->state);
233
 
234
        if (in_interrupt ())
235
                BUG ();
236
 
237
        hub = hcd->self.root_hub;
238
        if (HCD_IS_RUNNING (hcd->state))
239
                hcd->state = USB_STATE_QUIESCING;
240
 
241
        dev_dbg (hcd->controller, "roothub graceful disconnect\n");
242
        usb_disconnect (&hub);
243
 
244
        hcd->driver->stop (hcd);
245
        hcd_buffer_destroy (hcd);
246
        hcd->state = USB_STATE_HALT;
247
        pci_set_drvdata (dev, 0);
248
 
249
        free_irq (hcd->irq, hcd);
250
        if (hcd->driver->flags & HCD_MEMORY) {
251
                iounmap (hcd->regs);
252
                release_mem_region (pci_resource_start (dev, 0),
253
                        pci_resource_len (dev, 0));
254
        } else {
255
                release_region (pci_resource_start (dev, hcd->region),
256
                        pci_resource_len (dev, hcd->region));
257
        }
258
 
259
        usb_deregister_bus (&hcd->self);
260
}
261
EXPORT_SYMBOL (usb_hcd_pci_remove);
262
 
263
 
264
#ifdef  CONFIG_PM
265
 
266
/**
267
 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
268
 * @dev: USB Host Controller being suspended
269
 * @state: state that the controller is going into
270
 *
271
 * Store this function in the HCD's struct pci_driver as suspend().
272
 */
273
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
274
{
275
        struct usb_hcd          *hcd;
276
        int                     retval = 0;
277
 
278
        hcd = pci_get_drvdata(dev);
279
        dev_dbg (hcd->controller, "suspend D%d --> D%d\n",
280
                        dev->current_state, state);
281
 
282
        switch (hcd->state) {
283
        case USB_STATE_HALT:
284
                dev_dbg (hcd->controller, "halted; hcd not suspended\n");
285
                break;
286
        case USB_STATE_SUSPENDED:
287
                dev_dbg (hcd->controller, "hcd already suspended\n");
288
                break;
289
        default:
290
                /* remote wakeup needs hub->suspend() cooperation */
291
                // pci_enable_wake (dev, 3, 1);
292
 
293
                pci_save_state (dev, hcd->pci_state);
294
 
295
                /* driver may want to disable DMA etc */
296
                hcd->state = USB_STATE_QUIESCING;
297
                retval = hcd->driver->suspend (hcd, state);
298
                if (retval)
299
                        dev_dbg (hcd->controller, "suspend fail, retval %d\n",
300
                                        retval);
301
                else
302
                        hcd->state = USB_STATE_SUSPENDED;
303
        }
304
 
305
        pci_set_power_state (dev, state);
306
        return retval;
307
}
308
EXPORT_SYMBOL (usb_hcd_pci_suspend);
309
 
310
/**
311
 * usb_hcd_pci_resume - power management resume of a PCI-based HCD
312
 * @dev: USB Host Controller being resumed
313
 *
314
 * Store this function in the HCD's struct pci_driver as resume().
315
 */
316
int usb_hcd_pci_resume (struct pci_dev *dev)
317
{
318
        struct usb_hcd          *hcd;
319
        int                     retval;
320
 
321
        hcd = pci_get_drvdata(dev);
322
        dev_dbg (hcd->controller, "resume from state D%d\n",
323
                        dev->current_state);
324
 
325
        if (hcd->state != USB_STATE_SUSPENDED) {
326
                dev_dbg (hcd->controller, "can't resume, not suspended!\n");
327
                return -EL3HLT;
328
        }
329
        hcd->state = USB_STATE_RESUMING;
330
 
331
        pci_set_power_state (dev, 0);
332
        pci_restore_state (dev, hcd->pci_state);
333
 
334
        /* remote wakeup needs hub->suspend() cooperation */
335
        // pci_enable_wake (dev, 3, 0);
336
 
337
        retval = hcd->driver->resume (hcd);
338
        if (!HCD_IS_RUNNING (hcd->state)) {
339
                dev_dbg (hcd->controller, "resume fail, retval %d\n", retval);
340
                usb_hc_died (hcd);
341
        }
342
 
343
        return retval;
344
}
345
EXPORT_SYMBOL (usb_hcd_pci_resume);
346
 
347
#endif  /* CONFIG_PM */
348
 
349