?? usb-ohci-pci.c
字號:
#include <linux/module.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/interrupt.h> /* for in_interrupt() */#undef DEBUG#include <linux/usb.h>#include "usb-ohci.h"#ifdef CONFIG_PMAC_PBOOK#include <asm/machdep.h>#include <asm/pmac_feature.h>#include <asm/pci-bridge.h>#ifndef CONFIG_PM#define CONFIG_PM#endif#endif/*-------------------------------------------------------------------------*//* Increment the module usage count, start the control thread and * return success. */static struct pci_driver ohci_pci_driver;static int __devinithc_found_ohci (struct pci_dev *dev, int irq, void *mem_base, const struct pci_device_id *id){ u8 latency, limit; ohci_t * ohci; int ret; printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); /* bad pci latencies can contribute to overruns */ pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); if (latency) { pci_read_config_byte (dev, PCI_MAX_LAT, &limit); if (limit && limit < latency) { dbg ("PCI latency reduced to max %d", limit); pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit); latency = limit; } } ret = hc_add_ohci(dev, irq, mem_base, id->driver_data, &ohci, ohci_pci_driver.name, dev->slot_name); if (ret == 0) ohci->pci_latency = latency; return ret;}/*-------------------------------------------------------------------------*/#ifdef CONFIG_PM/* controller died; cleanup debris, then restart *//* must not be called from interrupt context */static void hc_restart (ohci_t *ohci){ int temp; int i; if (ohci->pci_latency) pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); ohci->disabled = 1; ohci->sleeping = 0; if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); /* empty the interrupt branches */ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; /* no EDs to remove */ ohci->ed_rm_list [0] = NULL; ohci->ed_rm_list [1] = NULL; /* empty control and bulk lists */ ohci->ed_isotail = NULL; ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); } else dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);}#endif /* CONFIG_PM *//*-------------------------------------------------------------------------*//* configured so that an OHCI device is always provided *//* always called with process context; sleeping is OK */static int __devinitohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id){ unsigned long mem_resource, mem_len; void *mem_base; if (pci_enable_device(dev) < 0) return -ENODEV; if (!dev->irq) { err("found OHCI device with no IRQ assigned. check BIOS settings!"); return -ENODEV; } /* we read its hardware registers as memory */ mem_resource = pci_resource_start(dev, 0); mem_len = pci_resource_len(dev, 0); if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { dbg ("controller already in use"); return -EBUSY; } mem_base = ioremap_nocache (mem_resource, mem_len); if (!mem_base) { err("Error mapping OHCI memory"); return -EFAULT; } /* controller writes into our memory */ pci_set_master (dev); return hc_found_ohci (dev, dev->irq, mem_base, id);} /*-------------------------------------------------------------------------*//* may be called from interrupt context [interface spec] *//* may be called without controller present *//* may be called with controller, bus, and devices active */static void __devexitohci_pci_remove (struct pci_dev *dev){ ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); dbg ("remove %s controller usb-%s%s%s", hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), dev->slot_name, ohci->disabled ? " (disabled)" : "", in_interrupt () ? " in interrupt" : "" ); hc_remove_ohci(ohci); release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));}#ifdef CONFIG_PM/*-------------------------------------------------------------------------*/static intohci_pci_suspend (struct pci_dev *dev, u32 state){ ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); unsigned long flags; u16 cmd; if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); return -EIO; } /* act as if usb suspend can always be used */ info ("USB suspend: usb-%s", dev->slot_name); ohci->sleeping = 1; /* First stop processing */ spin_lock_irqsave (&usb_ed_lock, flags); ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); writel (ohci->hc_control, &ohci->regs->control); writel (OHCI_INTR_SF, &ohci->regs->intrstatus); (void) readl (&ohci->regs->intrstatus); spin_unlock_irqrestore (&usb_ed_lock, flags); /* Wait a frame or two */ mdelay(1); if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) mdelay (1); #ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) disable_irq (ohci->irq); /* else, 2.4 assumes shared irqs -- don't disable */#endif /* Enable remote wakeup */ writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); /* Suspend chip and let things settle down a bit */ ohci->hc_control = OHCI_USB_SUSPEND; writel (ohci->hc_control, &ohci->regs->control); (void) readl (&ohci->regs->control); mdelay (500); /* No schedule here ! */ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { case OHCI_USB_RESET: dbg("Bus in reset phase ???"); break; case OHCI_USB_RESUME: dbg("Bus in resume phase ???"); break; case OHCI_USB_OPER: dbg("Bus in operational phase ???"); break; case OHCI_USB_SUSPEND: dbg("Bus suspended"); break; } /* In some rare situations, Apple's OHCI have happily trashed * memory during sleep. We disable it's bus master bit during * suspend */ pci_read_config_word (dev, PCI_COMMAND, &cmd); cmd &= ~PCI_COMMAND_MASTER; pci_write_config_word (dev, PCI_COMMAND, cmd);#ifdef CONFIG_PMAC_PBOOK { struct device_node *of_node; /* Disable USB PAD & cell clock */ of_node = pci_device_to_OF_node (ohci->ohci_dev); if (of_node) pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); }#endif return 0;}/*-------------------------------------------------------------------------*/static intohci_pci_resume (struct pci_dev *dev){ ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); int temp; unsigned long flags; /* guard against multiple resumes */ atomic_inc (&ohci->resume_count); if (atomic_read (&ohci->resume_count) != 1) { err ("concurrent PCI resumes for usb-%s", dev->slot_name); atomic_dec (&ohci->resume_count); return 0; }#ifdef CONFIG_PMAC_PBOOK { struct device_node *of_node; /* Re-enable USB PAD & cell clock */ of_node = pci_device_to_OF_node (ohci->ohci_dev); if (of_node) pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); }#endif /* did we suspend, or were we powered off? */ ohci->hc_control = readl (&ohci->regs->control); temp = ohci->hc_control & OHCI_CTRL_HCFS;#ifdef DEBUG /* the registers may look crazy here */ ohci_dump_status (ohci);#endif /* Re-enable bus mastering */ pci_set_master(ohci->ohci_dev); switch (temp) { case OHCI_USB_RESET: // lost power info ("USB restart: usb-%s", dev->slot_name); hc_restart (ohci); break; case OHCI_USB_SUSPEND: // host wakeup case OHCI_USB_RESUME: // remote wakeup info ("USB continue: usb-%s from %s wakeup", dev->slot_name, (temp == OHCI_USB_SUSPEND) ? "host" : "remote"); ohci->hc_control = OHCI_USB_RESUME; writel (ohci->hc_control, &ohci->regs->control); (void) readl (&ohci->regs->control); mdelay (20); /* no schedule here ! */ /* Some controllers (lucent) need a longer delay here */ mdelay (15); temp = readl (&ohci->regs->control); temp = ohci->hc_control & OHCI_CTRL_HCFS; if (temp != OHCI_USB_RESUME) { err ("controller usb-%s won't resume", dev->slot_name); ohci->disabled = 1; return -EIO; } /* Some chips likes being resumed first */ writel (OHCI_USB_OPER, &ohci->regs->control); (void) readl (&ohci->regs->control); mdelay (3); /* Then re-enable operations */ spin_lock_irqsave (&usb_ed_lock, flags); ohci->disabled = 0; ohci->sleeping = 0; ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { if (ohci->ed_controltail) ohci->hc_control |= OHCI_CTRL_CLE; if (ohci->ed_bulktail) ohci->hc_control |= OHCI_CTRL_BLE; } writel (ohci->hc_control, &ohci->regs->control); writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* Check for a pending done list */ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); (void) readl (&ohci->regs->intrdisable); spin_unlock_irqrestore (&usb_ed_lock, flags);#ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) enable_irq (ohci->irq);#endif if (ohci->hcca->done_head) dl_done_list (ohci, dl_reverse_done_list (ohci)); writel (OHCI_INTR_WDH, &ohci->regs->intrenable); writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ break; default: warn ("odd PCI resume for usb-%s", dev->slot_name); } /* controller is operational, extra resumes are harmless */ atomic_dec (&ohci->resume_count); return 0;}#endif /* CONFIG_PM *//*-------------------------------------------------------------------------*/static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { /* * AMD-756 [Viper] USB has a serious erratum when used with * lowspeed devices like mice. */ vendor: 0x1022, device: 0x740c, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, driver_data: OHCI_QUIRK_AMD756,} , { /* handle any USB OHCI controller */ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), class_mask: ~0, /* no matter who makes it */ vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, }, { /* end: all zeroes */ }};MODULE_DEVICE_TABLE (pci, ohci_pci_ids);static struct pci_driver ohci_pci_driver = { name: "usb-ohci", id_table: &ohci_pci_ids [0], probe: ohci_pci_probe, remove: __devexit_p(ohci_pci_remove),#ifdef CONFIG_PM suspend: ohci_pci_suspend, resume: ohci_pci_resume,#endif /* PM */}; /*-------------------------------------------------------------------------*/static int __init ohci_hcd_init (void) { return pci_module_init (&ohci_pci_driver);}/*-------------------------------------------------------------------------*/static void __exit ohci_hcd_cleanup (void) { pci_unregister_driver (&ohci_pci_driver);}module_init (ohci_hcd_init);module_exit (ohci_hcd_cleanup);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -